Разработка приложений для Internet
В 24 и 28 томах серии " Библиотека системного программиста" мы начали рассказывать о программировании приложений Windows с использованием Microsoft Visual C++ и библиотеки классов MFC. Вы узнали об основных принципах построения приложений MFC, использовании диалоговых панелей, меню, панелях управления и панелях состояния, получили общее представление о работе с базами данных.В этом томе мы продолжим изучение библиотеки классов MFC и ее возможностей. Теперь мы расскажем о использовании Microsoft Visual C++ и библиотеки MFC для разработки приложений Internet и Intranet. Мы также затронем технологию ActiveX и расскажем о том как использовать в своих приложениях готовые органы управления OLE и ActiveX. В приложениях, представленных в данной книге, мы будем активно работать с диалоговыми панелями. На примере списка с пиктограммами и линейного индикатора вы научитесь использовать стандартные органы управления Windows 95 и Windows NT. Мы также приведем начальные сведения о использовании мультизадачности в приложениях MFC. Одно из наших приложений будет создавать дополнительную задачу, выполняющую загрузку файлов с сервера FTP в фоновом режиме.
До недавнего времени создание приложений, которые могут работать с ресурсами сети Internet или Intranet, являлось достаточно сложной задачей. Программист должен был использовать интерфейс сокетов Windows или программировать на более низком уровне протокола TCP\IP.
Чтобы облегчить тяжелый труд программиста, Microsoft разработала новый программный интерфейс Win32 Internet, получивший красивое название WinInet. С использованием WinInet значительно облегчается создание приложений, работающих с протоколами HTTP, FTP и системой Gopher. При этом программист избавляется не только от необходимости программирования на уровне сокетов Windows или протокола TCP\IP, но и от самостоятельной реализации протоколов HTTP и FTP.
Фактически WinInet представляет собой набор функций, реализующих протоколы HTTP (Hypertext Transfer Protocol), FTP (File Transfer Protocol), а также включающих средства для работы с системой Gopher.
В состав библиотеки MFC начиная с версии 4.2 включен целый ряд классов, надстроенных над программным интерфейсом WinInet. Мы рассмотрим использование этих классов на примере нескольких приложений, предназначенных для работы с серверами FTP и WWW.
Приложение FtpView, рассмотренное в этой книге, позволяет соединиться с сервером FTP, просмотреть структуру каталогов сервера и даже загрузить с сервера нужные вам файлы. Модифицируя приложение FtpView мы продемонстрируем различные приемы работы с серверами FTP.
Так как загрузка файлов с сервера, особенно большого размера, может занять слишком много времени, мы расскажем о том, как можно организовать получение файла в фоновом режиме с помощью отдельной задачи. Попутно вы узнаете о том, как приложение, построенное с использованием библиотеки классов MFC, может создать несколько задач и организовать их взаимодействие.
В отдельном разделе мы рассмотрим взаимодействие с серверами WWW. На примере приложения Console HTTP мы покажем как с использованием WinInet можно передать серверу WWW запрос и получить от него интересующую вас информацию или текст страниц WWW.
Естественно, даже с использованием классов WinInet, сложно создать полноценное приложение, которое позволяет просматривать страницы WWW. Достаточно того, что такое приложение должно не только правильно интерпретировать все операторы языка HTML, но также исполнять программы на языке JAVA, и правильно отображать органы управления ActiveX.
Вы, однако, можете очень быстро создать настоящий навигатор Internet, если воспользуетесь органом управления Microsoft Web Browser Control. На основе этого органа управления построен навигатор Microsoft Internet Explorer.
Microsoft Web Browser Control представляет собой орган управления ActiveX, который вы можете включить в свое приложение и использовать его как другие органы управления Windows.
Мы приведем исходный текст приложения Look, использует Microsoft Web Browser Control для просмотра серверов Internet. Наше приложение представляет собой маленький навигатор Internet Explorer, который обладает практически теми же возможностями, что и настоящий навигатор Microsoft Internet Explorer.
В заключение мы расскажем о том, как ваше приложение может использовать Microsoft Internet Explorer, Netscape Navigator и другие навигаторы для просмотра ресурсов сети Internet или Intranet. Таким образом, например, можно организовать получение из Internet самой последней справочной информации по вашему приложению.
Интерфейс WinInet
Программный интерфейс WinInet предназначен для разработки 32-разрядных приложений. К сожалению, вы не сможете воспользоваться WinInet при создании 16-разрядных приложений для операционных систем Windows 3.хх.В принципе вы можете применять приложения WinInet в среде операционных систем Windows 3.хх, если дополнительно установите Win32s. Однако в этом случае вы не должны пользоваться библиотекой классов MFC, потому что классы для работы с программным интерфейсом WinInet добавлены в MFC версии 4.2, а начиная с этой версии, приложения, подготовленные в Microsoft Visual C++ и MFC, не будут работать в среде Windows 3.хх с установленной поддержкой 32-разрядных приложений Win32s.
Функции WinInet являются реентерабельными. Поэтому вы свободно можете использовать их в многозадачных приложениях и не заботиться о синхронизации отдельных задач между собой.
В настоящее время функции WinInet располагаются в библиотеке динамической компоновки WININET.DLL. Эта библиотека поставляется вместе с новыми версиями операционных системам Windows.
Полную документацию по функциям программного интерфейса WinInet вы можете получить на сервере WWW фирмы Microsoft по адресу: http://www.microsoft.com/win32dev.
В нешей книге мы не станем рассмотривать функции программного интерфеса WinInet. Вместо этого мы изучим классы MFC, которые интегрируют в себе все возможности этого программного интерфейса.
Взаимодействие с сервером FTP
Серверы FTP предоставляют однин из самых распространенных типов сервиса Internet. Сервера FTP обычно хранят на своих дисках большое количество различных файлов, доступных пользователям сервера. Администратор сервера FTP может разграничить доступ к серверу FTP различным категориям пользователей. Так например, для всех желающих, которые подключаются к серверу FTP под именем anonymus, могут быть доступны только некоторые каталоги, содержащие файлы общего доступа. Чтобы получить доступ к другим файлам, администратор сервера должен зарегистрировать вас и дать вам имя и пароль для подключения к серверу.В зависимости от того, какими правами вы обладаете, вы можете иметь право не только загружать файлы с сервера, но также и записывать файлы со своего компьютера на сервер FTP, а также изменять структуру каталогов самого сервера - создавать, удалять и переименовывать каталоги.
Надо отметить, что все операции, связанные с реорганизацией структуры каталогов сервера и даже с загрузкой данных на сервер как правило, недоступны обычным пользователям. Их выполняют только администратор сервера и его помощники. Поэтому, если у вас нет собственного FTP сервера, могут возникнуть трудности с отладкой приложений загружающих файлы на сервер. Если в вашем распоряжении есть локальная сеть, мы рекомендуем установить на ней свой сервер FTP и отлаживать свои программы в рамках локальной сети.
Как и любое другое приложение, использующее для работы с Internet классы WinInet, вы должны первым делом создать сеанс связи представленный объектом класса CInternetSession.
Затем вы должны выполнить соединение с сервером FTP. Для этого надо вызвать метод GetFtpConnection класса CInternetSession. Методу GetFtpConnection надо указать адрес сервера FTP. Если указанный сервер не будет обнаружен в сети, то данный метод вызовет исключение и завершится с ошибкой. Это может быть следствием того, что вы неправильно указали имя сервера или сервер не активен в данный момент времени.
В случае успешного завершения, метод GetFtpConnection создает объект класса CFtpConnection. Этот объект будет представлять в вашем приложении указанный сервер FTP.
Вызывая методы класса CFtpConnection ваше приложение может определить и изменить текущий каталог сервера, выполнять поиск файлов и каталогов с определенными именами, обмениваться с сервером файлами, удалять и переименовывать файлы, изменять структуру каталогов - создавать, переименовывать и удалять каталоги и т. д.
Так, чтобы определить и изменить путь текущего каталога на сервере FTP надо воспользоваться методами GetCurrentDirector и SetCurrentDirectory класса CFtpConnection. Создать каталог вы можете при помощи метода CreateDirectory, удалить с помощью метода RemoveDirectory. Другие методы класса CFtpConnection, предназначенные для работы с системой каталогов сервера, вы можете просмотреть в разделе “Методы для управления каталогами”.
Чтобы загрузить файл с сервера FTP вы можете обратится к методу GetFile, а чтобы загрузить файл на сервер - к методу PutFile класса CFtpConnection. Эти методы работают как команда COPY операционной системы MS-DOS. Вы указываете им только имя файла на диске локального компьютера и имя файла на сервере. Все остальное они делают самостоятельно.
Вы также можете открыть файл на сервере FTP с помощью метода OpenFile класса CFtpConnection. Если указанный файл удалось открыть, этот метод возвращает объект класса CInternetFile. Далее вы можете использовать методы Read и Write данного класса чтобы прочитать или записать открытый файл.
Если у вас есть соответствующие права на данном сервере FTP, вы можете переименовать файлы на сервере или удалить их. Для этого надо использовать методы Rename и Remove класса CFtpConnection.
Как только приложение установило соединение с сервером FTP, вы можете просмотреть содержимое указанного каталога или попытаться найти на сервере файлы с определенным именем. Для этого надо создать объект класса CFtpFileFind, указав конструктору класса объект класса CFtpConnection, представляющий соединение с сервером. Затем с помощью методов FindFile и FindNextFile вы можете осуществить поиск файлов и каталогов с указанными именами. С помощью методов класса CFileFind, являющегося базовым классом для CFtpFileFind, вы можете определить различные характеристики обнаруженных файлов и каталогов.
В следующем разделе мы предложим вашему вниманию приложение ConsoleFtp, которое выполняет соединение с указанным сервером FTP. Вы можете использовать исходный текст этого приложения как шаблон для изучения основных приемов работы с серверами FTP.
Взаимодействие с сервером WWW
Сервера WWW предоставляют пользователям возможность просмотра гипертекстовой информации. Вся информация, расположенная на сервере, разделена на небольшие страницы.Когда вы соединяетесь с сервером WWW с помощью какой-нибудь программы навигатора, например Microsoft Internet Explorer или Netscape Navigator, то вы увидите, что страницы могут содержать не только форматированный текст и гипертекстовые ссылки, но также рисунки, аудио и видео информацию. Некоторые страницы даже могут содержать органы управления диалоговых панелей - поля ввода, кнопки, переключатели.
Как же удается сочетать столь разнообразную информацию в одном документе? Оказывается сами страницы, адрес которых вы указываете навигатору, содержат описание на языке разметки гипертекста HTML (Hyper Text Markup Language) и хранятся в файлах с расширениями htm или html. Навигатор загружает файл страницы, а затем интерпретирует его и отображает на экране. При этом он, возможно, будет загружать и другие файлы с сервера, например файлы изображений или анимации.
В листинге 3.1 представлен пример страницы WWW, содержащей в себе текст, изображение и ссылки на другие страницы WWW (это главная рускоязычная страница нашего сервера WWW).
Листинг 3.1. Файл main.htm
Добро пожаловать на наш сервер WEB!
На этом сервере вы можете найти подробную информацию
о наших книгах из серий "Персональный компьютер.
Шаг за шагом" и "Библиотека системного программиста":
Эти серии книг предназначенны для самого широкого
круга людей, чья работа связана с компьютерами.
Мы подготовили для вас и кое-что еще!
Эту страницу лучше всего просматривать навигаторами MS Internet Explorer v3.0 или Netscape Navigator
ALT="MS Internet Explorer v3.0" BORDER=0> | ![]() |
Посылайте ваши комментарии по адресу
frolov@glas.apc.org
© Александр Фролов, Григорий Фролов, 1997
Как видите, исходный текст страницы WWW, хранящийся на сервере, имеет мало общего с изображением этой странице в окне навигатора. Чтобы вывести страницу на экран, навигатор проделывает большую работу. Он загружает все необходимые файлы изображений, аудио и видео, которые включены в данную страницу, а затем отображает страницу на экране, руководствуясь операторами языка HTML.
¨ Описание языка HTML и процедуры создания страниц WWW вы можете найти в 29 томе серии “Библиотека системного программиста”, который называется “Сервер WEB своими руками”.
Самостоятельная обработка исходных файлов HTML достаточно проста, хотя и требует очень большой и кропотливой работы, так как по сути, вам будет нужно создать интерпретатор языка HTML. Чтобы не загромождать исходные тексты наших приложений, мы не будем обрабатывать полученные страницы WWW, а основное внимание уделим процедуре взаимодействия с сервером.
Если ваше приложение должно загружать с сервера страницы WWW и отображать их на экране, не выполняя дополнительной обработки, вы можете воспользоваться органом управления Microsoft Web Browser Control. Мы расскажем про этот орган управления в главе “Ваш собственный Internet Explorer”.
Сначала необходимо инициализировать сеанс связи с Internet. Для этого надо создать объект класса CInternetSession, или класса наследованного от CInternetSession.
Затем вы должны открыть соединение с сервером WWW. Для этого надо вызвать метод GetHttpConnection класса CInternetSession. Методу GetHttpConnection указывается адрес сервера WWW. Он создает объект класса CHttpConnection. Этот объект будет представлять в вашем приложении указанный сервер WWW.
В отличие от метода GetFtpConnection класса CInternetSession, рассмотренного нами ранее, GetHttpConnection на самом деле не выполняет настоящего соединения с сервером. Поэтому даже если указать ему имя несуществующего сервера, он создаст объект класса CHttpConnection без сообщения об ошибке. Настоящее соединение с сервером будет установлено значительно позже, во время передачи серверу WWW запроса.
Теперь надо создать (открыть) и подготовить запрос для передачи серверу. Для этого вызвите метод OpenRequest класса CHttpConnection. Он создаст объект класса CHttpFile. Вы можете добавить к созданному запросу дополнительные заголовки. Для этого надо воспользоваться методом AddRequestHeaders класса CHttpFile.
Когда запрос полностью сформирован, его следует передать серверу с помощью метода SendRequest класса CHttpFile. Этот метод передает запрос указанному серверу. Именно в это время устанавливается сетевое соединение. Если запрос передан успешно, тогда метод SendRequest возвращает значение TRUE. Если указанный сервер не существует или на нем отсутствует запрашиваемый файл - возвращается значение FALSE и может быть вызвано исключение CInternetException.
Далее следует получить ответ на запрос от сервера. Для этого вызовите метод QueryInfo класса CHttpFile. В некоторых случаях, когда надо только проверить существует ли страница с указанным адресом этого уже достаточно.
Если же вам необходимо загрузить страницу с сервера на локальный компьютер, вы можете воспользоваться методом Read. Этот метод определен в классе CInternetFile, который является базовым для класса CHttpFile.
Если во время чтения файла прервется связь с сервером или произойдут другие непредвиденные события, то метод Read может вызвать исключение CInternetException. Чтобы приложение правильно обрабатывало ошибки WinInet, вы должны поместить все методы, которые могут вызвать исключение в блок try и определить для него соответствующий блок catch.
В следующем разделе мы предложим вашему вниманию приложение ConsoleHTTP, которое выполняет соединение с указанным сервером WWW. Используйте исходный текст этого приложения как шаблон для изучения основных приемов работы с серверами WWW.
Ваш собственный Internet Explorer
Для просмотра серверов Internet используются специальные программы - навигаторы. Сегодня вы можете встретить много различных навигаторов, однако наиболее часто используются Microsoft Internet Explorer и Netscape Navigator.Навигаторы представляют собой весьма сложные программы. Достаточно сказать, что они должны уметь работать с протоколами передачи данных TCP/IP, протоколом передачи гипертекста HTTP, протоком передачи файлов FTP. Навигатор также должен уметь правильно отображать принятые данные на экране. Он должен понимать данные в формате HTML, уметь отображать на экране графические файлы, использовать органы управления ActiveX, исполнять аплеты на языке Java и т. д. (рис. 4.1).

Рис. 4.1. Microsoft Internet Explorer
Основу навигатора Microsoft Internet Explorer составляют несколько библиотек dll, в которых определены объекты ActiveX. Именно они несут на себе основную нагрузку - взаимодействуют с серверами, отображают страницы WWW на экране и т. д. Окно навигатора вместе с меню панелями управления и состояния - не более чем простая оболочка и не несет на себе никакой функциональной нагрузки.
Самое замечательное в этом то, что вы можете воспользоваться ядром Microsoft Internet Explorer и в своих собственных приложениях. Для этого достаточно подключить соответствующий орган управления Microsoft Web Browser Control к проекту и вы сможете использовать его как другие органы управления, например кнопки, поля редактирования, списки и т. д.
В следующем разделе мы представим вашему вниманию приложение Look. В нем мы будем использовать орган управления Microsoft Web Browser Control, чтобы создать свой маленький, но работающий навигатор Internet.
Ссылки на ресурсы Internet
Во многих случаях нет необходимости встраивать навигатор непосредственно в приложение. Как правило достаточно ограничиться возможностью запуска навигатора из программы с переходом к нужному ресурсу.Примером такого приложения может служить Microsoft Visual C++. Выверите из меню Help строку Web Favorites. На экране появится диалоговая панель Web Favorites (рис. 5.1). В списке Name перечислены ссылки на различные ресурсы Internet, которые могут быть интересны для программистов. Вы можете выбрать название любого ресурса из списка и нажать на кнопку Go To чтобы получить к нему доступ. Загрузится Internet Explorer, или другой навигатор, установленный в компьютере, и вы сможете просмотреть выбранный ресурс.

Рис. 5.1. Диалоговая панель Web Favorites
Кнопки New, Edit и Delete служат для редактирования списка ресурсов Internet. Они позволяют добавить новую ссылку, отредактировать уже существующую ссылку и удалить ссылку из списка.
Для загрузки ресурса Internet можно воспользоваться функциями ShellExecute или WinExec. В качестве примера мы привели приложение WebHelp. Справочная информация для приложения WebHelp доступна через Internet по адресу http://www.dials.ccas.ru/frolov/rwin/webhelp.htm.
Адреса URL
Ресурсы, доступные через сеть Internet, определяются посредством универсальных адресов ресурсов или просто адресов (URL - Universal Resource Locators или Uniform Resource Locators). Адрес URL определяет имя сервера на котором расположен ресурс, имя объекта и каталог где этот ресурс находится, протокол для взаимодействия с сервером и номер порта TCP/IP на котором происходит соединение.Общий формат адресов URL представлен ниже:
service://server:port/dir/dir/object.ext
Поле service определяет протокол для работы с сервером. Сразу после названия протокола следует символ двоеточия и два обратных слеша.
Затем идет поле server. Оно содержит название сервера, соответствующего данному адресу.
После имени сервера может располагаться номер порта TCP/IP. Это числовое значение перед которым надо указать символ двоеточия. Данное поле необязательное. Если вы не укажете номер порта TCP/IP, то будет использоваться порт принятый по умолчанию для данного протокола.
Непосредственно за номером порта TCP/IP или сразу после имени сервера (если номер порта TCP/IP не задан) следует путь каталога и имя объекта на который ссылается адрес. В качестве имени объекта может фигурировать имя файла, имя расширения CGI или ISAPI.
Асинхронный режим WinInet
К сожалению, классы WinInet имеют один недостаток. Большинство операций по взаимодействию с сетью Internet занимают много времени. Даже процедура соединения с сервером может занять несколько десятков секунд. Мы уже не говорим о загрузки с серверов FTP файлов большого размера. Даже если вы имеете соединение с Internet через высокоскоростной модем, получение файла с размерами в несколько мегабайт займет несколько десятков минут.В самом простом случае классы WinInet используются в синхронном режиме, вызывая соответствующие методы классов непосредственно из основной задачи приложения. Естественно, пользовательский интерфейс приложения в этом случае блокируется. Мало того, что пользователь не сможет работать с таким приложением во время загрузки файла, если вы переключитесь в это время на другое приложение, открыв его окно поверх окна приложения работающего с Internet, а затем попытаетесь переключится обратно, то окно приложения не будет восстановлено.
Иллюстрацией вышесказанного может быть приложение FtpView, описанное нами в разделе “Загрузка файлов с сервера FTP”. Соединитесь с его помощью с каким-либо сервером FTP и попытайтесь загрузить файл большого размера. Вы увидите что в этом случае пользовательский интерфейс приложения блокируется и окно приложения не перерисовывается.
Существует две возможности выхода из этой ситуации.
Во-первых, можно организовать в рамках приложения еще одину задачу, и все вызовы методов WinInet, которые могут занять много времени выполнять именно из нее. При этом основная задача приложения останется свободеной и пользовательский интерфейс блокироваться не будет.
Во-вторых, можно использовать WinInet в асинхронном режиме. При этом методы классов WinInet будут сразу возвращать управление еще до того как соответствующая операция завершится. Возникает вопрос, как же приложение узнает о том что длительная асинхронная операция наконец-то завершилась и можно продолжить работу приложения далее?
Программный интерфейс WinInet извещает приложение о завершении асинхронной операции через специальную функцию обратного вызова. Если вы используете классы MFC, управляющие WinInet, вы можете воспользоваться виртуальным методом OnStatusCallback класса CInternetSession, который вызывается функцией обратного вызова WinInet.
¨ Метод OnStatusCallback также может использоваться и в том случае, если вы вызываете методы WinInet в синхронном режиме. В этом случае OnStatusCallback позволит вашему приложению сообщать пользователю более подробную информацию о исполнении текущей операции
Чтобы воспользоваться методом OnStatusCallback, вы должны наследовать от класса CInternetSession собственный класс, переопределив в нем этот метод.
Конструктор класса CInternetSession имеет параметр, через который ему передаются флаги, управляющие режимами работы данного сеанса связи. Если вы желаете чтобы операции данного сеанса связи исполнялись в асинхронном режиме, вы должны указать конструктору флаг INTERNET_FLAG_ASYNC.
После того как вы создадите объект этого класса, представляющий сеанс связи, вы должны вызвать метод EnableStatusCallback чтобы разрешить использование метода OnStatusCallback.
Теперь дальнейшие вызовы методов классов WinInet могут завершиться с ошибкой ERROR_IO_PENDING, означающей что операция выполняется в асинхронном режиме. Когда асинхронная операция завершится, будет вызван метод OnStatusCallback с кодом завершения INTERNET_STATUS_REQUEST_COMPLETE. Сразу заметим, что метод OnStatusCallback вызывается не только по завершении асинхронной операции. Этот метод также может вызываться раньше и сигнализирует о выполнении какой либо конкретной фазы асинхронной операции.
Метод OnStatusCallback имеет несколько параметров, которые определяют текущее состояние исполняемой операции WinInet. Один и тот же метод OnStatusCallback вызывается для всех асинхронных операций, исполняемых в рамках данного сеанса связи. Чтобы иметь возможность различать, какая операция вызвала метод OnStatusCallback, ему передается идентификатор контекста. Идентификатор контекста является числом типа DWORD и указывается при вызове многих методов классов WinInet. По умолчанию, идентификатор контекста принимается равным единице.
Теперь, когда вы получили общее представление об устройстве приложений WinInet, мы опишем соответствующие классы MFC. Мы будем подробно рассматривать только наиболее важные методы этих классов, которые впоследствии будут использоваться в наших приложениях. Описание остальных методов вы найдете в справочной системе Microsoft Visual C++.
Мы также рекомендуем вам ознакомиться с исходными текстами интересующих вас методов. В них вы сможете найти ответы на многие свои вопросы. Некоторые программисты даже считают исходные тексты MFC самой лучшей документацией к этой библиотеке. Вам также потребуется описание программного интерфейса WinInet, которое вы можете найти на сервере WWW Microsoft по адресу http://www.microsoft.com/workshoop/prog/sdk/docs/wininet/.
Благодарности
Большую помощь нам оказал сотрудник Microsoft АО Юрий Тумашко, предоставивший для работы над этой книгой дистрибутив Microsoft Visual C++ версии 4.2.Авторы выражают благодарность Фроловой Ольге Викторовне, Кустову Виктору. Мы также благодарим всех сотрудников издательского отдела АО "ДИАЛОГ-МИФИ": Голубева Олега Александровича, Дмитриеву Наталью, Виноградову Елену, Кузьминову Оксану.
Поддержку в работе над книгами нам оказали генеральный директор АО “ДиалогНаука” Антимонов Сергей Григорьевич и руководитель антивирусного отдела АО “ДиалогНаука” Лященко Юрий Павлович, предоставившие доступ к сети Internet и позволившие нам разместить у них свой сервер WWW.
Блокировка кнопок
Теперь нам надо добавить программный код, выполняющий блокировку кнопок. Для этого мы будем использовать метод EnableWindow базового класса CWnd (Класс CButton наследуется от базового класса CWnd).BOOL EnableWindow(BOOL bEnable = TRUE);
Этот метод имеет единственный необязательный параметр, который задает новое состояние кнопки. Если параметр bEnable не указан или равен TRUE, тогда кнопка становится доступна, а если FALSE - кнопка блокируется.
Значение второго параметра метода OnCommandStateChangeExplorer, принадлежащего классу CBrowserDlg, можно непосредственно передавать параметру bEnable метода EnableWindow данной кнопки.
Переопределите метод OnCommandStateChangeExplorer класса CBrowserDlg, опять же используя для этого средства MFC ClassWizard. Загрузите полученный шаблон метода OnCommandStateChangeExplorer в редактор и дополните его в соответствии со следующим листингом:
void CBrowserDlg::OnCommandStateChangeExplorer(
long Command,
BOOL Enable
)
{
switch(Command)
{
case CSC_NAVIGATEFORWARD:
// Меняем состояние кнопки Forward
m_Forward.EnableWindow(Enable);
break;
case CSC_NAVIGATEBACK:
// Меняем состояние кнопки Back
m_Back.EnableWindow(Enable);
break;
}
}
В представленном выше программном коде мы не проверяем значение параметра Command на равенство значению CSC_UPDATECOMMANDS. Метод OnCommandStateChangeExplorer вызывается с этим значением, если изменилось состояние кнопок панели управления.
Постройте полученное приложение и запустите его на выполнение. Теперь вы заметите, что кнопки Back и Forward будут автоматически блокироваться по мере необходимости и предупреждающее сообщение, представленное на рисунке 3.3 больше не появится.
После прочтения главы об органе управления Microsoft Web Browser Control у вас может появиться вполне резонный вопрос - а зачем вообще нужно создавать свою программу просмотра, если можно просто использовать готовое приложение Internet Explorer без всякого дополнительного программирования.
Безусловно, создавать свой Internet Explorer не имеет особого смысла. Эта задача успешно решена Microsoft. Однако, если вы разрабатываете приложения для компьютерной сети в рамках своего предприятия, преимущества налицо. Вы, например, можете легко интегрировать средства WWW в свое приложение и позволить пользователю просматривать только определенные ресурсы сети.
Чтение файла с серверов сети Internet
В этом случае вы можете сразу вызвать метод OpenURL класса CInternetSession. Он выполнит соединение с указанным ему сервером и создаст объект класса CInternetFile, соответствующий необходимому вам файлу.Далее вы можете воспользоваться методами класса CInternetFile, чтобы прочитать данный файл с сервера на локальный компьютер. Для этой цели вы можете вызвать метод Read, который читает файл и записывает считанные данные в буфер, или метод ReadString, который считывает из файла текстовую строку, ограниченную символом перевода строки.
Описанный метод получения файла не позволяет выполнить обратную задачу - записать файл на сервер FTP. Серверы WWW и Gopher такую операцию не допускают в любом случае. От них вы можете только получать информацию.
Деструктор класса CFtpViewDlg
Когда пользователь нажимает кнопку OK и закрывает диалоговую панель, вызывается деструктор класса CFtpViewDlg. Он последовательно закрывает соединение с сервером FTP и завершает сеанс связи с Internet.Деструктор проверяет установлено ли соединение с сервером FTP - при этом указатель m_FtpConnection должен быть не равен значению NULL. Если это так, сначала вызывается метод Close, закрывающий соединение, а затем удаляется сам объект m_FtpConnection:
if (m_FtpConnection != NULL)
{
m_FtpConnection -> Close();
delete m_FtpConnection;
}
После того, как соединение с сервером FTP закрыто, завершаем сеанс связи с Internet. Для этого вызываем метод Close, а затем удаляем объект m_InternetSession:
if (m_InternetSession != NULL)
{
m_InternetSession -> Close();
delete m_InternetSession;
}
В завершение деструктор класса CFtpViewDlg удаляет список изображений m_ImageList:
delete m_ImageList;
Для самостоятельного изучения
Естественно, что самую полную и свежую информацию о программировании для Internet можно найти на серверах этой сети. Приложив немного усилий, вы легко получите из Internet сведения о самых последних разработках ведущих фирм, занимающихся программным обеспечением.В сети вы найдете описания различных стандартов протоколов, программных интерфейсов, технические статьи и даже примеры программ с исходными текстами. Для поиска информации в сети мы рекомендуем вам воспользоваться специальными поисковыми серверами. Адреса наиболее популярных поисковых серверов вы найдете в следующей таблице:
| Поисковый сервер | Адрес | ||
| Lycos | http://www.lycos.com | ||
| Magellan | http://www.magellan.com | ||
| Yahoo | http://www.yahoo.com | ||
| Microsoft | http://www.microsoft.com/search/ | ||
| AltaVista | http://www.altavista.com | ||
| Excite | http://www.excite.com | ||
| NetGuide | http://www.netguide.com |
Сервер WWW фирмы Microsoft содержит специальную страницу, позволяющую провести поиск данных с использованием нескольких (на выбор) поисковых серверов. Адрес этой страницы http://home.microsoft.com/access/allinone.asp. Если у вас установлен навигатор Microsoft Internet Explorer, то вы можете перейти на эту страницу, выбрав из меню Go строку Search the Web, или просто нажав кнопку Search в главной панели инструментов.
Навигатор Microsoft Internet Explorer, орган управления Microsoft Web Browser Control, программный интерфейс WinInet и набор классов MFC для управления WinInet, ActiveX SDK - это далеко не полный перечень разработок Microsoft, связанных с технологиями Internet. Вы можете получить информацию или даже сами эти продукты непосредственно с серверов WWW и FTP фирмы Microsoft. Ниже мы приводим несколько адресов, которые будут вам полезны:
| Адрес | Содержание | ||
| http://www.microsoft.com/visualc/ | Страница Microsoft Visual C++ | ||
| http://www.microsoft.com/intdev/sdk/ | Описание органа управления Microsoft Web Browser Control | ||
| http://www.microsoft.com/ie/ | Навигатор Microsoft Internet Explorer | ||
| http://www.microsoft.com/intdev/sdk/ | ActiveX SDK | ||
| http://www.microsoft.com/intdev/sdk/ | Документация по программному интерфейсу WinInet |
Большое количество полезных документов расположено на сервере ds.internic.net. Здесь вы найдете описание протокола HTTP, универсальных ресурсов URL, URI и URN, формата MIME, системы доменных имен и множество другой полезной информации. Документам присвоены номера RFC, указанные в названии соответствующих файлов. Список этих документов расположен в файле http://ds.internic.net/rfc/rfc.txt.
Следующая табица содержит адреса и краткие описания ряда документов RFC:
|
Адрес |
Содержание |
|
http://ds.internic.net/rfc/rfc1630.txt |
Описание формата URI |
|
http://ds.internic.net/rfc/rfc1738.txt |
Описание формата URL |
|
http://ds.internic.net/rfc/rfc1945.txt |
Описание протокола HTTP 1.0 |
|
http://ds.internic.net/rfc/rfc1341.txt |
Формат MIME |
|
http://ds.internic.net/rfc/rfc1034.txt |
Доменная система имен |
¨ Путешествуя в сети Internet по указанным нами адресам, вы должны иметь в виду, что серверы могут быть закрыты, могут сменить адрес или тематику
В Internet существует множество серверов, посвященных программистам, использующим Microsoft Visual C++ и библиотеку классов MFC. На них вы найдете ответы на вопросы, статьи и другую полезную информацию. В следующей таблице представлены некоторые адреса таких серверов:
|
Адрес |
Содержание |
|
http://www.iftech.com |
Обучение |
|
http://www.mcp.com |
Издательство |
|
http://www.visionx.com |
Сервер The MFC Professional |
|
http://www.vcdj.com/default.htm |
Журнал Visual C++ Developers Journal |
Добавление программного кода
После того как вы подготовили главную диалоговую панель приложения Look и привязали к органам управления этой панели соответствующие переменные, надо добавить к классу CLookDlg ряд методов, которые будут обрабатывать сообщения от диалоговой панели. Вы должны добавить методы, обрабатывающие сообщения от кнопок Navigate (IDC_NAVIGATE), Stop (IDC_BUTTON_STOP), Refresh (IDC_BUTTON_REFRESH), Exit (IDOK), от списка с адресами серверов Internet (IDC_COMBO_ADDRESS) и от объекта навигатора (IDC_EXPLORER).Чтобы добавить все эти методы мы рекомендуем вам воспользоваться средствами MFC ClassWizard. Для этого надо запустить MFC ClassWizard, в диалоговой панели MFC ClassWizard выбрать страницу Message Map. Затем из списка Class name выберите имя класса CLookDlg. К этому классу мы будем добавлять все наши методы.
Из списка Object IDs выбирайте идентификаторы кнопок, списка и навигатора, сообщения от которых надо обрабатывать. При этом в списке Messages будет отображаться список сообщений, которые этот орган управления вырабатывает и методов которые вызываются при определенных условиях. Чтобы добавить новый метод, выберите из списка Messages идентификатор сообщения или название метода которые надо обработать или переопределить и нажмите кнопку Add Function. Вам будет предложено определить название метода. Вы можете оставить его по умолчанию или изменить по своему усмотрению.
Так, вы должны добавить к классу CLookDlg методы для обработки командных сообщений BN_CLICKED от кнопок Navigate (IDC_NAVIGATE), Stop (IDC_BUTTON_STOP), Refresh (IDC_BUTTON_REFRESH). Примите предложение MFC ClassWizard и назовите соответствующие методы именами OnButtonNavigate, OnButtonStop и OnButtonRefresh.
Для списка IDC_COMBO_ADDRESS вы должны назначить обработчик сообщения с кодом идентификации CBN_SELCHANGE. Это сообщение передается диалоговой панели, когда пользователь выбирает из списка новый элемент. Назовите этот метод OnSelchangeComboAddress.

Рис. 4.11. Орган управления Microsoft Web Browser и MFC ClassWizard
Теперь вам надо переназначить ряд методов, которые вызываются навигатором IDC_EXPLORER. Выберите из списка Object IDs идентификатор IDC_EXPLORER. В списке Messages появятся названия методов, которые вы можете переназначить (рис. 4.11). Обратите внимание, когда вы выбираете строки из списка Messages, то ниже списка Member function в поле Description отображается краткое описание данного метода.
Вы должны переопределить следующие методы - DownloadBegin, DownloadComplete, ProgressChange, BeforeNavigate, FrameBeforeNavigate, TitleChange, Quit, StatusTextChange и NavigateComplete. Для каждого из них MFC ClassWizard предложит собственное название. Согласитесь со всеми предложенными названиями. В следующей таблице мы привели соответствие названий событий в списке Messages и названий методов, которые их обрабатывают:
|
Строка Messages |
Метод-обработчик |
|
DownloadBegin |
OnDownloadBeginExplorer |
|
DownloadComplete |
OnDownloadCompleteExplorer |
|
ProgressChange |
OnProgressChangeExplorer |
|
TitleChange |
OnTitleChangeExplorer |
|
StatusTextChange |
OnStatusTextChangeExplorer |
|
NavigateComplete |
OnNavigateCompleteExplorer |
Определение класса CLookDlg содержится во включаемом файле LookDlg.h. Мы привели исходный текст файла LookDlg.h в листинге 4.7.
Листинг 4.7. Файл LookDlg.h
//////////////////////////////////////////////////////////////
// CLookDlg dialog
//{{AFX_INCLUDES()
#include "webbrowser.h"
//}}AFX_INCLUDES
class CLookDlg : public CDialog
{
// Construction
public:
CLookDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CLookDlg)
enum { IDD = IDD_LOOK_DIALOG };
CProgressCtrl m_Progress;
CWebBrowser m_explorer;
CString m_address;
CString m_StatusText;
CString m_TitleBar;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CLookDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CLookDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnSelchangeComboAddress();
afx_msg void OnButtonRefresh();
afx_msg void OnButtonStop();
afx_msg void OnButtonBack();
afx_msg void OnButtonNext();
afx_msg void OnDownloadBeginExplorer();
afx_msg void OnDownloadCompleteExplorer();
afx_msg void OnProgressChangeExplorer(long Progress,
long ProgressMax);
afx_msg void OnTitleChangeExplorer(LPCTSTR Text);
afx_msg void OnStatusTextChangeExplorer(LPCTSTR Text);
afx_msg void OnNavigate();
afx_msg void OnNavigateCompleteExplorer(LPCTSTR URL);
DECLARE_EVENTSINK_MAP()
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Вы должны самостоятельно доработать все новые методы, только что добавленные MFC ClassWizard к классу CLookDlg, в соответствии с листингом 3.3.
Определение методов класса CLookDlg и таблица сообщений этого класса содержатся в файле LookDlg.cpp. Кроме того файл LookDlg.cpp содержит конструкцию, которая ранее не встречалась в наших приложениях.
Исходный текст файла LookDlg.cpp приведен нами в листинге 4.8.
Листинг 4.8. Файл LookDlg.cpp
#include "stdafx.h"
#include "Look.h"
#include "LookDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////
// Конструктор класса CLookDlg
CLookDlg::CLookDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLookDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLookDlg)
m_address = _T("");
m_StatusText = _T("");
m_TitleBar = _T("");
//}}AFX_DATA_INIT
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
//////////////////////////////////////////////////////////////
// Метод DoDataExchange класса CLookDlg.
// Организует взаимодействие между органами управления
// диалоговой панели и привязанными к ним элементами класса
void CLookDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLookDlg)
DDX_Control(pDX, IDC_PROGRESS, m_Progress);
DDX_Control(pDX, IDC_EXPLORER, m_explorer);
DDX_CBString(pDX, IDC_COMBO_ADDRESS, m_address);
DDX_Text(pDX, IDC_STATUS_TEXT, m_StatusText);
DDX_Text(pDX, IDC_TITLE_BAR, m_TitleBar);
//}}AFX_DATA_MAP
}
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CLookDlg
BEGIN_MESSAGE_MAP(CLookDlg, CDialog)
//{{AFX_MSG_MAP(CLookDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_CBN_SELCHANGE(IDC_COMBO_ADDRESS,
OnSelchangeComboAddress)
ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh)
ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
ON_BN_CLICKED(IDC_BUTTON_BACK, OnButtonBack)
ON_BN_CLICKED(IDC_BUTTON_NEXT, OnButtonNext)
ON_BN_CLICKED(IDC_NAVIGATE, OnNavigate)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////
// Метод OnInitDialog класса CLookDlg
BOOL CLookDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
return TRUE;
}
//////////////////////////////////////////////////////////////
// Метод OnPaint класса CLookDlg
void CLookDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND,
(WPARAM) dc.GetSafeHdc(), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//////////////////////////////////////////////////////////////
// Метод OnQueryDragIcon класса CLookDlg
HCURSOR CLookDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
//////////////////////////////////////////////////////////////
// Метод OnSelchangeComboAddress класса CLookDlg.
// Вызывается при выборе нового адреса из списка
void CLookDlg::OnSelchangeComboAddress()
{
// Определяем адрес, выбранный из списка
UpdateData(TRUE);
// Переходим к просмотру соответствующего ресурса
m_explorer.Navigate(
m_address, // Адрес URL для просмотра
NULL, // Дополнительные флаги
NULL, // Имя (frame name) ресурса
NULL, // Данные для запроса POST
NULL // Заголовок запроса HTTP
);
}
//////////////////////////////////////////////////////////////
// Метод OnButtonRefresh класса CLookDlg. Обновить
// текущую страницу WWW, которая отображается навигатором
void CLookDlg::OnButtonRefresh()
{
m_explorer.Refresh();
}
//////////////////////////////////////////////////////////////
// Метод OnButtonStop класса CLookDlg.
// Прервать загрузку страницы WWW
void CLookDlg::OnButtonStop()
{
m_explorer.Stop();
}
//////////////////////////////////////////////////////////////
// Метод OnButtonBack класса CLookDlg.
// Вернуться к просмотру предыдущей страницы
void CLookDlg::OnButtonBack()
{
m_explorer.GoBack();
}
//////////////////////////////////////////////////////////////
// Метод OnButtonStop класса CLookDlg.
// Передти к просмотру следующей страницы
void CLookDlg::OnButtonNext()
{
m_explorer.GoForward();
}
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CLookDlg.
//
BEGIN_EVENTSINK_MAP(CLookDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CLookDlg)
ON_EVENT(CLookDlg, IDC_EXPLORER, 106 /* DownloadBegin */,
OnDownloadBeginExplorer, VTS_NONE)
ON_EVENT(CLookDlg,IDC_EXPLORER, 104 /* DownloadComplete */,
OnDownloadCompleteExplorer, VTS_NONE)
ON_EVENT(CLookDlg, IDC_EXPLORER, 108 /* ProgressChange */,
OnProgressChangeExplorer, VTS_I4 VTS_I4)
ON_EVENT(CLookDlg, IDC_EXPLORER, 100 /* BeforeNavigate */,
OnBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
VTS_PVARIANT VTS_BSTR VTS_PBOOL)
ON_EVENT(CLookDlg,IDC_EXPLORER,200 /*FrameBeforeNavigate*/,
OnFrameBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
VTS_PVARIANT VTS_BSTR VTS_PBOOL)
ON_EVENT(CLookDlg, IDC_EXPLORER, 113 /* TitleChange */,
OnTitleChangeExplorer, VTS_BSTR)
ON_EVENT(CLookDlg, IDC_EXPLORER, 103 /* Quit */,
OnQuitExplorer, VTS_PBOOL)
ON_EVENT(CLookDlg,IDC_EXPLORER, 102 /* StatusTextChange */,
OnStatusTextChangeExplorer, VTS_BSTR)
ON_EVENT(CLookDlg, IDC_EXPLORER, 101 /*NavigateComplete */,
OnNavigateCompleteExplorer, VTS_BSTR)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
//////////////////////////////////////////////////////////////
// Метод OnDownloadBeginExplorer класса CLookDlg.
// Начало загрузки файла
void CLookDlg::OnDownloadBeginExplorer()
{
// Инициализируем линейный индикатор
m_Progress.SetRange(0, 100);
m_Progress.SetStep(0);
m_Progress.SetPos(0);
}
//////////////////////////////////////////////////////////////
// Метод OnDownloadCompleteExplorer класса CLookDlg.
// Загрузка завершена, сбрасываем линейный индикатор
void CLookDlg::OnDownloadCompleteExplorer()
{
// TODO: Add your control notification handler code here
m_Progress.SetPos(0);
}
//////////////////////////////////////////////////////////////
// Метод OnProgressChangeExplorer класса CLookDlg.
// Идет загрузка, обновляем положение линейного индикатора
void CLookDlg::OnProgressChangeExplorer(long Progress, long ProgressMax)
{
// На всякий случай проверяем параметры метода
if(Progress <= 0 | ProgressMax <= 0)
return;
// Изменяем положение линейного индикатора
m_Progress.SetPos( (int) (Progress * 100) / ProgressMax);
}
//////////////////////////////////////////////////////////////
// Метод OnTitleChangeExplorer класса CLookDlg.
// Получен текст для заголовка окна навигатора
void CLookDlg::OnTitleChangeExplorer(LPCTSTR Text)
{
// Отображаем заголовок в поле редактирования IDC_TITLE_BAR
SetDlgItemText(IDC_TITLE_BAR, Text);
}
//////////////////////////////////////////////////////////////
// Метод OnTitleChangeExplorer класса CLookDlg.
// Получен текст для панели состояния окна навигатора
void CLookDlg::OnStatusTextChangeExplorer(LPCTSTR Text)
{
// Отображаем текст в поле редактирования IDC_STATUS_TEXT
SetDlgItemText(IDC_STATUS_TEXT, Text);
}
//////////////////////////////////////////////////////////////
// Метод OnNavigate класса CLookDlg.
// Передти к просмотру заданного адреса URL
void CLookDlg::OnNavigate()
{
// Определяем адрес URL, выбранный пользователем
UpdateData(TRUE);
// Переходим к просмотру ресурса по заданному адресу
m_explorer.Navigate(
m_address, // Адрес URL
NULL,
NULL,
NULL,
NULL
);
}
//////////////////////////////////////////////////////////////
// Метод OnNavigateCompleteExplorer класса CLookDlg.
// Загрузка всех ресурсов страницы завершена
void CLookDlg::OnNavigateCompleteExplorer(LPCTSTR URL)
{
// Обновляем адрес URL ресурса, показанного навигатором
m_address = URL;
UpdateData(FALSE);
}
Функции WinInet
Кроме классов WinInet в состав библиотеки MFC входят три глобальные функции - AfxParseURL, AfxGetInternetHandleType и AfxThrowInternetException. Они не принадлежат классам MFC и могут вызываться из любого места приложения.Наибольший интерес представляет функция AfxParseURL. Она позволяет выделить из строки URL составные части. Вы можете использовать эту функцию для обработки адресов URL.
Для обработки ошибок WinInet в состав библиотеки классов MFC включен класс CInternetException. Соответствующее исключение вызывается методами классов WinInet, чтобы сообщить приложению о возникших ошибках и нестандартных ситуациях. Приложение может самостоятельно вызвать исключение CInternetException, воспользовавшись глобальной функцией AfxThrowInternetException.
Функция AfxGetInternetHandleType позволяет определить тип идентификатора Internet. Для определения типа идентификатора Internet глобальная функция InternetQueryOption вызывает функцию InternetQueryOption библиотеки WinInet.
Также как и классы WinInet все глобальные функции описаны в файле AFXINET.H. Если вы желаете изучить их более подробно, то можете найти исходные тексты в файле INET.CPP.
Функция AfxParseURL
Функция AfxParseURL разбирает текстовую строку, содержащую универсальный указатель ресурсов URL и выделяет из него тип сервиса, объект и номер порта TCP/IP. Приведем прототип функции AfxParseURL:BOOL AFXAPI
AfxParseURL(
LPCTSTR pstrURL,
DWORD& dwServiceType,
CString& strServer,
CString& strObject,
INTERNET_PORT& nPort
);
Указатель на строку, содержащую URL, передается функции AfxParseURL через параметр pstrURL. Функция AfxParseURL разбирает данную строку и возвращает результат через параметры dwServiceType, strServer, strObject и nPort.
Тип сервиса, соответствующий указанному URL, записывается в переменную по ссылке dwServiceType. В качестве типа сервиса может фигурировать одна из констант, перечисленных в следующей таблице (полный список смотрите в документации Microsoft Visual C++):
| Константа | Тип сервиса | ||
| AFX_INET_SERVICE_FILE | Имя файла на локальном диске компьютера | ||
| AFX_INET_SERVICE_FTP | Протокол передачи файлов FTP | ||
| AFX_INET_SERVICE_GOPHER | Протокол Gopher | ||
| AFX_INET_SERVICE_HTTP | Протокол передачи гипертекста | ||
| AFX_INET_SERVICE_MAILTO | Адрес электронной почты (e-Mail) | ||
| AFX_INET_SERVICE_NEWS | Новости | ||
| AFX_INET_SERVICE_NNTP | Новости с использованием протокола NNTP | ||
| AFX_INET_SERVICE_TELNET | Протокол Telnet | ||
| AFX_INET_SERVICE_WAIS | Протокол Wais |
В строку strServer записывается первое поле URL, определяющее тип сервиса (тип протокола). Объект на который ссылается URL записывается в строку strObject. И, наконец, номер порта TCP/IP записывается в переменную nPort.
Функция AfxParseURL возвращает ненулевое значение в случае успешного завершения и нуль в случае ошибки.
Функция ShellExecute
Функция ShellExecute выполняет различные действия над определенным файлом. Файл может быть либо исполнимым файлом, либо файлом документа, либо папкой. Функция может запустить файл на выполнение (для исполняемых файлов), открыть файл документа с помощью соответствующего ему приложения или раскрыть на экране окно с содержимым папки (каталога).Рассмотрим прототип функции ShellExecute:
HINSTANCE ShellExecute(
HWND hwnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,
INT nShowCmd
);
Параметр hwnd указывает идентификатор окна. Это окно будет выступать в качестве родительского окна для запускаемого приложения.
Наибольший интерес представляет параметр lpOperation. Он определяет операцию, которая будет выполняться над файлом, заданным в параметре lpFile. Как ни странно, код операции задается не числовой константой, а строкой, закрытой нулем. В качестве lpOperation можно указать строки “open”, “print” и “explore”. Краткое описание соответствующих операций мы привели в следующей таблице:
| Параметр lpOperation | Операция | ||
| “open” или значение NULL | Открыть файл, указанный параметром lpFile. Файл может быть исполнимым файлом, документом или папкой | ||
| “print” | Распечатать файл, определенный параметром lpFile. Указанный файл должен быть файлом документа | ||
| “explore” | Функция позволяет просмотреть содержимое папки, заданной параметром lpFile с помощью приложения Microsoft Explorer |
В случае, если ShellExecute используется для запуска исполнимого файла, вы можете передать ему строку параметров через lpParameters. Во всех остальных случаях указывайте в качестве lpParameters значение NULL.
Параметр lpDirectory задает имя каталога, который используется по умолчанию. В качестве него вы можете указать значение NULL.
Параметр nShowCmd позволяет указать режим, в котором будет запущено приложение. Этот параметр используется только при запуске исполнимых файлов. Если функция ShellExecute используется для открытия или печати файла документа, этот параметр следует указать равным нулю.
Описание возможных значений для параметра nShowCmd вы найдете в документации Microsoft Visual C++ или SDK. Фактически в качестве этого параметра можно использовать значения параметра nCmdShow известной функции WinMain. Например, вы можете указать в качестве параметра nShowCmd значение SW_SHOWNORMAL, который означает, что главное окно приложения должно иметь нормальные размеры и положение на экране, или значение SW_MINIMIZE, которое минимизирует окно запускаемого приложения.
Функция ShellExecute возвращает идентификатор (instance handle) запущенного приложения или идентификатор сервера DDE. Если возникнет ошибка, то функция возвращает ее код, значение которого меньше 32. Например, если указанный вами файл не будет обнаружен, то функция вернет код ERROR_FILE_NOT_FOUND. Полный список ошибок функции ShellExecute вы можете найти в документации.
Главный класс приложения
Метод InitInstance главного класса приложения выполняет инициализацию и, собственно, отображает на экране главную диалоговую панель приложения, которая управляется классом CLookDlg.В целом, метод InitInstance практически ни чем не отличается от аналогичных методов других приложений с пользовательским интерфейсом на основе диалоговой панели. Исключение составляет только вызов глобальной функции AfxEnableControlContainer:
AfxEnableControlContainer();
Эта функция вызывается сразу после того как метод InitInstance получает управление и позволяет работать приложению как контейнеру для органов управления ActiveX. Другими словами функция AfxEnableControlContainer позволяет вам использовать в приложении органы управления ActiveX (OLE).
HTML и справочная система Windows
В примере приложения WebHelp, справочная информация расположена в сети Internet на нашем WWW сервере по адресу http://www.glasnet.ru/~frolov. Однако вы можете пользоваться навигатором и для просмотра справочной информации в формате HTML на локальном диске компьютера, дискете или компакт диске.Вы можете использовать технологии Internet для создания справочной системы приложения в формате HTML. В принципе, такая справочная система способна заменить традиционную справочную систему Windows. Надо сказать, что так поступают многие фирмы, например Microsoft и Intel. Достаточно посмотреть компакт-диск технической поддержки Intel и последнюю версию “Решений Microsoft”, также распространяемых на компакт-диске.
В пользу использования справочной системы на основе технологий Internet можно привести несколько соображений. Во-первых, разработка справочной системы в формате HTML позволяет легко перенести ее с локального варианта на WWW сервер Internet и наоборот. Поэтому вы убиваете сразу двух зайцев - создаете справочную систему для распространения вместе с приложением и подготавливаете материал для размещения на своем сервере в Internet. Во-вторых, вы можете использовать в разработке справочной системы новые технологии, открывающие новые возможности, например, органы управления ActiveX, аплеты на языке Java и т. д. Большое количество приложений для создания файлов HTML значительно облегчит вам эту задачу.
Если вы решили разрабатывать справочную систему приложения на основе файлов в формате HTML, надо позаботится, чтобы вместе с приложением поставлялась программа-навигатор. Для этого, вы можете использовать, например, навигатор Microsoft Internet Explorer. Подробную информацию о возможностях лицензионного распространения этого навигаторе вы можете получить на WWW сервере корпорации Microsoft по адресу http://www.microsoft.com/ie/ieak/.
INTERNET_OPTION_ASYNC_ID
Идентификатор последнего асинхронного запроса, который выполнен в данной задаче.INTERNET_OPTION_CALLBACK
Адрес функции обратного вызова, установленной для данного сеанса.INTERNET_OPTION_CONNECT_BACKOFF
Задержка в миллисекундах между повторными попытками соединения.INTERNET_OPTION_CONNECT_RETRIES
Количество попыток соединения. В случае неудачной попытки соединения выполняется соответствующее количество повторных попыток. По умолчанию выполняется 5 попыток.INTERNET_OPTION_CONNECT_TIMEOUT
Время, отведенное на установление соединения. Задается в миллисекундах. Если соединение не устанавливается в течении данного времени, оно отменяется. По умолчанию время на соединение никак не ограничено.INTERNET_OPTION_CONTEXT_VALUE
Идентификатор контекста, связанный с данным идентификатором Internet.INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT
Время, отведенное для приема запроса, не связанного с получением данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.INTERNET_OPTION_CONTROL_SEND_TIMEOUT
Время, отведенное для передачи запроса, не связанного с передачей данных. Задается в миллисекундах. По умолчанию время на передачу запроса не ограниченоINTERNET_OPTION_DATA_RECEIVE_TIMEOUT
Время, отведенное для запроса на прием данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.INTERNET_OPTION_DATA_SEND_TIMEOUT
Время, отведенное для передачи запроса с данными. Задается в миллисекундах. По умолчанию время на передачу запроса не ограничено.INTERNET_OPTION_EXTENDED_ERROR
Код ошибки программного интерфейса Winsock, который вызвал ошибку ERROR_INTERNET_ в данной задаче приложения.Если метод QueryOption завершился без ошибок, он возвращает значение TRUE. В противном случае возвращается значение FALSE. Причину ошибки можно определить, обратившись к функции GetLastError.
INTERNET_OPTION_HANDLE_TYPE
Тип идентификатора Internet, например INTERNET_HANDLE_TYPE_CONNECT_FTP или INTERNET_HANDLE_TYPE_HTTP_REQUEST.INTERNET_OPTION_KEEP_CONNECTION
Определяет, используется ли данным идентификатором уже существующее соединение.INTERNET_OPTION_PASSWORD
Пароль, связанный с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.INTERNET_OPTION_READ_BUFFER_SIZE
Размер буфера, используемого для чтения данных с сервера FTP.INTERNET_OPTION_REQUEST_FLAGS
Информация о текущем процессе загрузки. Если загрузка идет из кэш-памяти, то возвращается флаг INTERNET_REQFLAG_FROM_CACHE.INTERNET_OPTION_USERNAME
Имя пользователя, связанное с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.INTERNET_OPTION_WRITE_BUFFER_SIZE
Размер буфера, используемого для передачи данных на сервер FTP.INTERNET_STATUS_CLOSING_CONNECTION
Соединение с сервером закрывается. Параметр lpvStatusInformation не используется.INTERNET_STATUS_CONNECTED_TO_SERVER
Установлено соединение по адресу сокета, указанного в буфере lpvStatusInformation.INTERNET_STATUS_CONNECTING_TO_SERVER
Выполняется соединение по адресу сокета, указанного в буфере lpvStatusInformation.INTERNET_STATUS_CONNECTION_CLOSED
Соединение с сервером закрыто. Параметр lpvStatusInformation не используется.INTERNET_STATUS_HANDLE_CREATED
Функция InternetConnect из программного интерфейса WinInet создала новый идентификатор.INTERNET_STATUS_NAME_RESOLVED
В буфере lpvStatusInformation находится IP-адрес сервера, имя которого вы указали.INTERNET_STATUS_RECEIVING_RESPONSE
Ожидает ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.INTERNET_STATUS_REQUEST_COMPLETE
Асинхронная операция завершена. Параметр lpvStatusInformation содержит значение NULL, но параметр dwStatusInformationLength используется. Он содержит код завершения асинхронной операции.Если параметр dwStatusInformationLength содержит значение ERROR_INTERNET_EXTENDED_ERROR, вы можете получить дополнительную информацию об ошибке, обратившись к функции InternetGetLastResponseInfo.
Если параметр dwStatusInformationLength имеет значение INTERNET_STATUS_REQUEST_COMPLETE, тогда параметр lpvStatusInformation содержит указатель на структуру типа INTERNET_ASYNC_RESULT.
Данная структура определена в файле wininet.h следующим образом:
typedef struct {
DWORD dwResult; // код завершения операции
DWORD dwError; // код ошибки
} INTERNET_ASYNC_RESULT, * LPINTERNET_ASYNC_RESULT;
В поле dwResult находится код завершения операции. Если во время выполнения операции возникла ошибка, тогда в поле dwError будет записан код ошибки. Если операция завершилась успешно, в поле dwError записана константа ERROR_SUCCESS.
¨ В том случае если вы используете библиотеку MFC в виде библиотеки dll, необходимо добавить в самом начале переопределенного вами метода OnStatusCallback строку следующего вида:
AFX_MANAGE_STATE( AfxGetStaticModuleState() );
INTERNET_STATUS_REQUEST_SENT
Информационный запрос передан на сервер. Параметр lpvStatusInformation не используется.INTERNET_STATUS_RESOLVING_NAME
Определяет IP-адрес имени, записанного в параметре lpvStatusInformation.INTERNET_STATUS_RESPONSE_RECEIVED
Получен ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.INTERNET_STATUS_SENDING_REQUEST
Передается информационный запрос на сервер. Параметр lpvStatusInformation не используется.Исходные тексты приложения FtpView
Когда вы закончите создание и доработку ресурсов приложения FtpView, надо приступить к доработке программных кодов приложения. Список файлов, составляющих проект FtpView, вы можете просмотреть в окне Project Workspace на странице FileView (рис. 2.7). Вы можете непосредственно внести все необходимые изменения в исходные файлы проекта, однако значительно лучше переложить часть работы на MFC ClassWizard. Средствами MFC ClassWizard вы сможете легко добавить к классам новые элементы данных и новые методы.
Рис. 2.7. Файлы проекта FtpView
Исходный текст приложения
Исходный текст приложения весьма прост и не содержит в себе каких-либо особенностей. Если вы желаете подробно изучить алгоритм работы приложения WebHelp, воспользуйтесь томом 24 серии “Библиотека системного программиста”. Единственное, что сейчас представляет для нас интерес, это то, как происходит запуск навигатора.Когда вы выбираете из меню Help строку Web Help приложению поступает командное сообщение с идентификатором ID_HELP_WEBHELP. Это сообщение поступает в класс CWebHelpWindow и распределяется таблицей сообщений этого класса методу WebHelpCommand:
BEGIN_MESSAGE_MAP(CWebHelpWindow, CFrameWnd)
ON_COMMAND(ID_HELP_WEBHELP, WebHelpCommand)
...
END_MESSAGE_MAP()
Метод WebHelpCommand передает функции ShellExecute адрес страницы WWW, содержащей справочную информацию для приложения. Этот вызов запускает навигатор для просмотра информации по указанному адресу:
IRes = ShellExecute( NULL, // Индекс родительского окна
"open", // Код операции
"http://www.dials.ccas.ru/" // URL адрес страницы
"frolov/rwin/webhelp.htm",
NULL, // Строка параметров
NULL, // Путь текущего каталога
SW_SHOWNORMAL // Режим отображения
// окна навигатора
);
Мы не указываем функции ShellExecute индекс родительского окна и используем вместо него значение NULL. Нам надо открыть файл HTML, поэтому в качестве второго параметра передается строка "open". Строка URL адреса, в которой записан путь к справочному файлу, передается функции через третий параметр. Следующие два параметра - строка параметров и путь текущего каталога - не используются. В них указываются значения NULL.
В качестве кода операции используем строку "open". Она означает, что мы желаем открыть объект, адрес которого содержится в третьем параметре функции ShellExecute. В данном случае приложение WebHelp обращается на страницу WWW, расположенную по адресу http://www.dials.ccas.ru/frolov/rwin/webhelp.htm.
Третий и четвертый параметры функции ShellExecute содержат нулевые значения, так как мы не используем дополнительные параметры и текущий каталог. Последний, пятый параметр функции равен SW_SHOWNORMAL. Эта константа означает, что приложение для просмотра WWW страницы запускается в окне нормального размера (не свернутым до размера пиктограммы и не открытым на весь экран).
Код результата, возвращаемого функцией ShellExecute, проверяется. В случае если произошла ошибка, ее код отображается на экране при помощи функции MessageBox.
Конечно, можно было бы запустить на выполнение непосредственно исполнимый файл программы навигатора, но в этом случае необходимо знать имя этого файла и суметь передать ему адрес, который надо посетить. Следует сказать, что одно только первое требование уже составляет определенную трудность, так как файлы навигаторов разных фирм, естественно, имеют различные названия, более того для разных версий одного и того же навигатора название файлов также может отличаться.
Элементы данных класса CFtpViewDlg
Когда вы с помощью MFC ClassWizard привязывали переменные к органам управления диалоговой панели, в класс были добавлены несколько элементов данных. Все они были занесены с специальный блок, отмеченный комментариями вида //}}AFX_DATA://{{AFX_DATA(CFtpViewDlg)
CEdit m_Status; // Поле Directory
CButton m_Ok; // Кнопка OK
CButton m_OnTop; // Кнопка On top
CButton m_Connect; // Кнопка Connect
CListCtrl m_FtpList; // Таблица с содержимым каталога
CString m_FtpAddress; // Поле адреса сервера FTP
//}}AFX_DATA
Эти элементы данных представляют собой объекты классов MFC, управляющие соответствующими им органами управления диалоговой панели. Например, объект m_Connect класса CButton, привязан к кнопке Connect. Вызывая различные методы для этого объекта вы можете управлять кнопкой Connect (блокировать ее, снимать блокировку и т. д.).
Элементы данных, определенные внутри блока AFX_DATA тесно соотносятся с методом DoDataExchange, который, собственно, и осуществляет их связь с органами управления диалоговой панели.
Другая группа элементов данных класса CFtpViewDlg определена после ключевого слова protected содержит пять объектов. Доступ к этим объектам открыт только для методов самого класса CFtpViewDlg:
protected:
HICON m_hIcon; // Пиктограмма приложения
CString sCurentDirectory; // Текущий каталог
CImageList* m_ImageList; // Список изображений
CFtpConnection* m_FtpConnection; // FTP сервер
CInternetSession* m_InternetSession; // Сеанс связи
Элемент m_hIcon используется для хранения идентификатора пиктограммы приложения. Этот элемент создан MFC AppWizard и не представляет для нас особого интереса. Остальные элементы данных класса добавлены нами через окно Project Workspace и мы рассмотрим их более подробно.
Полный путь каталога, содержимое которого отображается в списке на диалоговой панели, хранится в строке sCurentDirectory. Этот путь устанавливается, когда приложение соединяется с сервером FTP, а потом изменяется, когда вы входите в каталог, отображаемый в списке или выходите в каталог верхнего уровня, нажимая кнопку On top.
В списке IDC_FTP_LIST отображаются названия каталогов и файлов. Для наглядности мы выделяем их различными пиктограммами. Список изображений формируется во время инициализации диалоговой панели методом OnInitDialog и указатель на него записывается в элемент данных m_ImageList класса CFtpViewDlg.
Два последних элемента данных, входящих в класс CFtpViewDlg, наиболее интересны, так как используются для доступа к сети Internet и серверу FTP.
Элемент m_InternetSession является указателем на объект класса CInternetSession. Этот объект создается во время инициализации диалоговой панели с помощью метода OnInitDialog и требуется для дальнейшей работы с классами WinInet.
Элемент m_FtpConnection представляет собой указатель на объект класса CFtpConnection. Этот объект создается при установлении соединения с сервером FTP, которое выполняется методом OnConnect, когда пользователь нажимает кнопку Connect.
Элементы данных класса CInternetException
Класс CInternetException содержит два элемента данных - m_dwError и m_dwContext. Они позволяют узнать код ошибки и идентификатор контекста операции, ставшей причиной вызова исключения.Код ошибки содержится в элементе данных m_dwError. Причина вызова исключения может быть не связана с WinInet напрямую. В этом случае в качестве кода ошибки m_dwError будет фигурировать один из кодов ошибок Win32. Их список вы найдете в документации Visual C++.
Список кодов ошибок, связанных с WinInet, вы можете найти в документации по ActiveX SDK, а также в Internet по следующему адресу: http:\\www.microsoft.com/intdev. Ниже мы перечислили для примера несколько таких кодов и дали им краткие описания:
| Код ошибки | Описание | ||
| ERROR_INTERNET_TIMEOUT | Время, отведенное на выполнение запроса истекло | ||
| ERROR_INTERNET_INTERNAL_ERROR | Внутренняя ошибка WinInet | ||
| ERROR_INTERNET_LOGIN_FAILURE | Неудачная попытка соединения с сервером FTP | ||
| ERROR_FTP_DROPPED | Операция с сервером FTP не завершена из за разрыва сеанса связи |
Как работает приложение Look
Приложение Look имеет пользовательский интерфейс на основе диалоговой панели. Мы не будем рассматривать основные принципы устройства этого приложения. Соответствующую информацию вы можете получить из 24 тома серии “Библиотека системного программиста”. Больше внимания мы уделим прикладной части приложения, имеющей отношение к управлению навигатором Microsoft Web Browser Control.Как работает приложение ParseURL
Структура приложения ParseURL ничем не отличается от других приложений с пользовательским интерфейсом на основе диалоговой панели, сформированных средствами MFC AppWizard. Поэтому мы не будем останавливаться на описании устройства приложения, и рассмотрим только моменты, непосредственно связанные с разбором адресов URL и отображением полученной информации в диалоговой панели.Сразу после запуска, приложение ParseURL создает диалоговую панель IDD_PARSEURL_DIALOG и отображает ее на экране. Управление этой диалоговой панелью осуществляет класс CParseURLDlg, определенный в нашем приложении.
Конструктор класса CParseURLDlg инициализирует строки m_ObjectName, m_PortNumber, m_ServiceType, m_UrlAddress. Несколько позже, когда будет вызван метод OnInitDialog, мы запишем в строку m_UrlAddress пример адреса URL. Чтобы вывести этот адрес на экран, метод OnInitDialog вызывает метод UpdateData с параметром FALSE:
UpdateData(FALSE);
Когда пользователь нажимает кнопку Convert, командное сообщение от нее попадает в таблицу сообщений класса CParseURLDlg и для его обработки вызывается метод OnButtonConvert:
ON_BN_CLICKED(IDC_BUTTON_CONVERT, OnButtonConvert)
Метод OnButtonConvert опять вызывает метод UpdateData, но на этот раз в качестве параметра ему передается значение TRUE. Этот метод считывает данные из органов управления диалоговой панели и записывает их в соответствующие переменные. Вызов этого метода необходим, так как пользователь мог изменить адрес URL в поле URL Address диалоговой панели приложения:
UpdateData(TRUE);
Теперь в строке m_UrlAddress записан адрес URL, который необходимо разобрать на составные части. Для этого вызываем глобальную функцию AfxParseURL, передавая ей строку m_UrlAddress для разбора и переменные dwServiceType, m_ServerName, m_ObjectName, nPort. В них будет записан результат разбора адреса:
if (!AfxParseURL(m_UrlAddress, dwServiceType, m_ServerName,
m_ObjectName, nPort))
{
}
Обратите внимание, что имя сервера и имя объекта записываются непосредственно в переменные m_ServerName и m_ObjectName, привязанные к полям диалоговой панели. А вот тип сервиса (тип протокола) и номер порта записываются во временные переменные nPort и dwServiceType, так как их тип не соответствует строковому:
INTERNET_PORT nPort;
DWORD dwServiceType;
Если в строке m_UrlAddress записан неправильный адрес, то функция AfxParseURL возвращает нулевое значение. В этом случае мы выводим сообщение об ошибке и сбрасываем строки m_ObjectName, m_PortNumber, m_ServerName, m_ServiceType:
AfxMessageBox("AfxParseURL Error");
m_ObjectName = "";
m_PortNumber = "";
m_ServerName = "";
m_ServiceType = "";
Если адрес успешно разобран, мы проверяем тип сервиса dwServiceType и записываем в строку m_ServiceType соответствующий текст. Чтобы не загромождать приложение мы таким образом распознаем только четыре основных типа сервиса:
switch(dwServiceType)
{
case AFX_INET_SERVICE_FTP:
m_ServiceType = "AFX_INET_SERVICE_FTP";
break;
case AFX_INET_SERVICE_HTTP:
m_ServiceType = "AFX_INET_SERVICE_HTTP";
break;
case AFX_INET_SERVICE_GOPHER:
m_ServiceType = "AFX_INET_SERVICE_GOPHER";
break;
case AFX_INET_SERVICE_FILE:
m_ServiceType = "AFX_INET_SERVICE_FILE";
break;
default:
// Формируем строку из значения переменной dwServiceType
m_ServiceType.Format("%d", dwServiceType);
}
Если вы указали другой тип сервиса мы записываем в строку m_PortNumber соответствующее числовое значение. Для этого мы используем метод Format класса CString:
m_PortNumber.Format("%d", nPort);
¨ Метод Format класса CString очень удобен для записи в строку форматированных значений каких-либо переменных. По своему назначению и формату вызова данный метод напоминает хорошо известную вам функцию sprintf. Только в функции sprintf
строка, в которую записываются форматированные данные, определяется первым параметром, а метод Format вызывается для объекта представляющего эту строку.
После того, как все переменные, привязанные к полям редактирования диалоговой панели, заполнены, вызываем метод UpdateData с параметром FALSE. Он отображает их содержимое в диалоговой панели:
UpdateData(FALSE);
Как связаться с авторами
Авторы имеют почтовый адрес в сети GlasNet. Все свои замечания и предложения по содержанию книг серий "Библиотека системного программиста", а также "Персональный компьютер - шаг за шагом" вы можете присылать нам по следующему адресу:: frolov@glas.apc.org
Наш почтовый адрес доступен не только пользователям сети Internet. Абоненты других компьютерных сетей также могут передавать нам сообщения. Ниже мы приводим наш адрес в различных сетях:
| Глобальная сеть | Наш адрес | ||
| CompuServe | >internet:frolov@glas.apc.org | ||
| GlasNet | frolov | ||
| Relcom | frolov@glas.apc.org | ||
| UUCP | uunet!cdp!glas!frolov |
Вы также можете присылать свои пожелания почтой по адресу:
* Издательский отдел АО "ДИАЛОГ-МИФИ".
Индекс 115409, город Москва, улица Москворечье,
дом 31, корпус 2.
Приносим свои извинения за то, что не можем ответить на каждое письмо. Мы также не занимаемся рассылкой книг, дискет и исходных текстов к нашим книгам. По этому вопросу обращайтесь непосредственно в издательство “Диалог-МИФИ”.
Как устроено приложение FtpView
Основу приложения FtpView составляют два класса CFtpViewApp и CFtpViewDlg. Класс CFtpViewApp является основным классом приложения. Он выполняет инициализацию и отображает диалоговую панель приложения. Класс CFtpViewDlg управляет этой диалоговой панелью, а также соединяется с Internet для получения списка объектов на сервере FTP.На рисунке 2.13 представлена страница ClassView окна проекта Project Workspace. На ней вы можете просмотреть структуру классов нашего приложения.

Рис. 2.13. Классы приложения FtpView
Как вызвать исключение CInternetException - функция AfxThrowInternetException
Чтобы принудительно вызвать исключение CInternetException из своего приложения воспользуйтесь глобальной функцией AfxThrowInternetException:void AFXAPI AfxThrowInternetException(
DWORD dwContext,
DWORD dwError = 0
);
Функции AfxThrowInternetException передаются два параметра. Через параметр dwError передается код ошибки, а через параметр dwContext - идентификатор контекста операции, вызвавшей ошибку. По умолчанию в качестве кода ошибки используется нулевое значение. В этом случае функция AfxThrowInternetException определяет код ошибки самостоятельно, обращаясь к функции GetLastError программного интерфейса Windows.
Функция AfxThrowInternetException создает объект класса CInternetException, присваивает элементам m_dwError и m_dwContext этого объекта значения, полученные через параметры dwContext и dwError и вызывает исключение.
В режиме отладки данная функция выводит в окне Debug сообщение о вызове исключения CInternetException с соответствующим кодом ошибки - "Warning: throwing CInternetException for error...".
Класс CFileFind
Класс CFileFind предназначен для поиска файлов на локальном компьютере. Этот класс наследуется от базового класса CObject:CObject -> CFileFind -> CFtpFileFind
|
-> CGopherFileFind
Класс CFileFind служит базовым классом для двух классов WinInet - CFtpFileFind и CGopherFileFind, которые предназначены для поиска файлов в сети Internet на серверах FTP и Gopher.
В следующей таблице мы привели описание методов класса CFileFind:
| Метод | Описание | ||
| CFileFind | Конструктор класса CFileFind | ||
| GetLength | Определяет длину файла в байтах | ||
| GetFileName | Определяем имя файла с расширением | ||
| GetFilePath | Определяет полный путь файла | ||
| GetFileTitle | Определяет заголовок файла, то есть его имя не учитывая расширение | ||
| GetFileURL | Определяет адрес файла в формате URL | ||
| GetRoot | Определяет корневой каталог, в котором расположен файл | ||
| GetCreationTime | Определяет время создания файла | ||
| GetLastAccessTime | Определяет время последнего обращения к файлу | ||
| GetLastWriteTime | Определяет время последнего изменения файла | ||
| MatchesMask | Определяет состояние заданных атрибутов файла | ||
| IsDots | Определяет, состоит ли имя файла из символов "." или "..", которые означают каталог верхнего уровня | ||
| IsReadOnly | Определяет, является ли данный файл доступным только для чтения | ||
| IsDirectory | Определяет, является ли очередной объект, полученный методом FindNext, каталогом или файлом | ||
| IsCompressed | Определяет, является ли данный файл компрессованным | ||
| IsSystem | Проверяет, является ли файл системным | ||
| IsHidden | Определяет, является ли данный файл скрытым | ||
| IsTemporary | Позволяет распознать временные файлы | ||
| IsNormal | Определяет, является ли файл “нормальным”. Под словосочетанием “нормальный файл” понимаются файлы у которых не установлены другие атрибуты | ||
| IsArchived | Определяет является ли данный файл архивным | ||
| Close | Завершает поиск файлов | ||
| FindFile | Начинает поиск файла или файлов в указанном каталоге | ||
| FindNextFile | Продолжает поиск файла или файлов в указанном каталоге. Вы должны использовать этот метод после обращения к методу FindFile |
Класс CFtpConnection
Класс CFtpConnection наследуется от базового класса CInternetConnection:CObject -> CInternetConnection -> CFtpConnection
Класс CFtpConnection предназначен для соединения с серверами FTP. Методы этого класса позволяют приложению обмениваться файлами с сервером FTP, а также выполнять различные сервисные операции, такие как создание и удаление каталогов, переименование и удаление файлов и т. д.
Для класса CFtpConnection определен конструктор класса, но вам никогда не потребуется обращаться к нему напрямую. Объект CFtpConnection создается косвенно при вызове метода GetFtpConnection для объекта класса CInternetSession:
// Определяем указатель на объект класса CFtpConnection
CFtpConnection* m_FtpConnection = NULL;
// Пытаемся соединиться с сервером sServer
m_FtpConnection =
m_InternetSession -> GetFtpConnection( sServer );
Метод Close закрывает соединение с сервером FTP. Вы должны вызвать этот метод после завершения работы с объектом CFtpConnection. В принципе, вызов метода Close можно совсем опустить. В этом случае соединение будет разорвано автоматически деструктором класса CFtpConnection.
Класс CFtpFileFind
Класс CFtpFileFind наследуется от базового класса CFileFind и позволяет провести поиск файлов на сервере FTP.Для нас будут представлять интерес конструктор класса CFtpFileFind и все остальные три метода, входящие в состав этого класса. Название этих методов мы привели в следующей таблице:
| Метод | Описание | ||
| CFtpFileFind | Конструктор класса CFtpFileFind | ||
| FindFile | Начинает поиск файла или файлов на сервере FTP | ||
| FindNextFile | Продолжает поиск файла или файлов в указанном каталоге сервера FTP. Вы должны использовать этот метод после обращения к методу FindFile | ||
| GetFileURL | Определяет адрес файла в формате URL |
Если вы решите заняться разработкой приложения, взаимодействующего с серверами FTP, вы будете активно использовать все методы данного класса. Поэтому мы приведем их описания.
Класс CFtpViewApp
Главный класс приложения CFtpViewApp, определен в файле FtpView.h. Мы привели исходный текст этого файла в листинге 2.4. Класс CFtpViewApp наследуется от базового класса CWinApp. При этом переопределяется единственный виртуальный метод InitInstance, который выполняет инициализацию приложения и отображает на экране главную диалоговою панель приложения.Листинг 2.4. Файл FtpView.h
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // Включаемый файл содержащий
// Идентификаторы ресурсов приложения
//============================================================
// Определение класса CFtpViewApp
// Методы класса CDialogApp определены в файле FtpView.cpp
//============================================================
class CFtpViewApp : public CWinApp
{
public:
CFtpViewApp();
// Overrides
// В следующем блоке ClassWizard помещает описания
// переопределенных виртуальных методов класса
//{{AFX_VIRTUAL(CFtpViewApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CFtpViewApp)
// В этом блоке ClassWizard размещает описания методов
// класса. Не редактируйте содержимое этого блока вручную
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Определение конструктора класса CFtpViewApp, метода InitInstance, таблицы сообщений, а также определение объекта данного класса вы найдете в файле FtpView.cpp. Исходный текст этого файла мы представили в листинге 2.5. Заметим, что файлы FtpView.h и FtpView.cpp мы оставляем без изменения какими их создал MFC AppWizard.
Весь программный код, который будет взаимодействовать с сервером FTP и обслуживать диалоговую панель IDD_FTPVIEW_DIALOG, мы добавим к классу CFtpViewDlg. По большей части мы будем использовать для этого средства MFC ClassWizard, так что вручную вам надо будет ввести только исходный текст добавленных методов.
Листинг 2.5. Файл FtpView.cpp
//============================================================
// Приложение для просмотра структуры каталогов
// серверов FTP
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
//============================================================
// Файл содержит определение методов и таблицы сообщений
// главного класса приложения
//============================================================
// Включаемые файлы
#include "stdafx.h"
#include "FtpView.h"
#include "FtpViewDlg.h"
// Для отладочной версии приложения включается дополнительные
// определения
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//============================================================
// Таблица сообщений класса CFtpViewApp
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewApp, CWinApp)
//{{AFX_MSG_MAP(CFtpViewApp)
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
//============================================================
// Конструктор класса CFtpViewApp
//============================================================
CFtpViewApp::CFtpViewApp()
{
// TODO:
}
//////////////////////////////////////////////////////////////
// Объект главного класса приложения
CFtpViewApp theApp;
//============================================================
// Метод InitInstance класса CFtpViewApp.
// Выполняет инициализацию приложения
//============================================================
BOOL CFtpViewApp::InitInstance()
{
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
CFtpViewDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Нажата кнопка OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Нажата кнопка Cancel
}
// Так как диалоговая панель закрыта, возвращаем значение
// FALSE чтобы завершить приложение
return FALSE;
}
Класс CFtpViewDlg
Все изменения, которые мы будем вносить в наш проект затронут, в основном, только класса CFtpViewDlg. Это и понятно - именно к диалоговой панели IDD_FTPVIEW_DIALOG, которая управляется данным классом, мы добавили новые органы управления для взаимодействия с сервером FTP.Приложения, которые используют для доступа к ресурсам сети Internet классы MFC WinInet, должны иметь как минимум один объект класса CInternetSession. Этот объект представляет собой “сеанс” связи с Internet.
В окне Project Workspace щелкните правой клавишей мыши по имени класса CFtpViewDlg. Откроется контекстное меню, из которого следует выбрать строку Add Variable. На экране появится диалоговая панель Add Member Variable (рис. 2.8).

Рис. 2.8. Диалоговая панель Add Member Variable
В поле Variable Type введите имя класса CInternetSession, а в поле Variable Declaration имя нового элемента класса CFtpViewDlg - m_InternetSession. Переключатель Access переведите в положение Protect. Нажмите кнопку OK. В класс CFtpViewDlg будет добавлен новый элемент - объект класса CInternetSession. Так как мы выбрали тип доступа Protect, то этот элемент будет расположен в секции protected и будет доступен только методам данного класса.
Повторите описанную процедуру и добавьте к классу CFtpViewDlg еще три элемента - строку sCurentDirectory класса CString, указатель m_ImageList на класс CImageList и указатель m_FtpConnection на класс CFtpConnection.
С помощью MFC ClassWizard привяжите переменные к органам управления диалоговой панели IDD_FTPVIEW_DIALOG. Для этого откройте MFC ClassWizard, перейдите на страницу Member Variables. В поле Class name выберите имя класса CFtpViewDlg. Затем последовательно выбирайте идентификаторы органов управления из списка Control IDs, нажимайте кнопку Add Variable и в открывающейся панели Add Member Variable вводите имя, категорию и тип переменной.
Добавляя переменные, пользуйтесь рисунком 2.9. На нем видно, какие переменные привязаны к органам управления диалоговой панели. Только кнопка OK не имеет собственной переменной, так как она нам не понадобится.

Рис. 2.9. Диалоговая панель MFC ClassWizard
Перейдите в диалоговой панели MFC ClassWizard на страницу Message Maps и добавьте несколько методов-обработчиков сообщений от органов управления диалоговой панели IDD_FTPVIEW_DIALOG. Для кнопки Connect, имеющей идентификатор IDC_CONNECT, и кнопки On Top с идентификатором IDC_ON_TOP добавьте обработчики командного сообщения BN_CLICKED - методы OnConnect и OnOnTop. Они будут вызываться при нажатии на эти кнопки. Для списка IDC_FTP_LIST добавьте обработчик сообщения NM_DBLCLK - метод OnDblclkFtpList. Он будет вызываться при двойном щелчке левой кнопкой мыши внутри списка.
Теперь остается совсем немного. Надо добавить метод DirectoryView и деструктор к классу CFtpViewDlg. MFC ClassWizard в этом вам не помощник. Откройте окно Project Workspase, если оно закрыто, и щелкните правой клавишей мыши по названию класса CFtpViewDlg. Откроется контекстное меню, из которого надо выбрать строку Add Function. На экране появится диалоговая панель Add Member Function (рис. 2.10).

Рис. 2.10. Диалоговая панель Add Member Function
Чтобы добавить метод DirectoryView, введите в поле Function Declaration его название, а в поле Function Type - тип значения, возвращаемого этим методом - BOOL. Повторите описанную операцию еще один раз и добавьте к классу CFtpViewDlg деструктор ~CFtpViewDlg. Для этого введите в поле Function Declaration строку ~CFtpViewDlg, а поле Function Type оставьте пустым.
MFC ClassWizard создает только шаблоны методов. Рабочий программный код вы должны добавить в них самостоятельно. Ниже мы привели исходные тексты файлов FtpViewDlg.h и FtpViewDlg.cpp, в соответствии с которыми вам надо доработать свой проект.
Класс CFtpViewDlg определен в файле FtpViewDlg.h. Мы привели исходный текст этого файла в листинге 2.6. Для этого файла вам надо добавить директивы define, определяющие четыре константы - COL_NUM, MIN_LEN_BUF, DIRECTORY и FILE. Эти константы будут использоваться методами класса FtpViewDlg.
Листинг 2.6. Файл FtpViewDlg.h
//============================================================
// Определение класса FtpViewDlg и некоторых констант
//============================================================
// Определение констант
#define COL_NUM 4 // Количество колонок таблицы
#define MIN_LEN_BUF 30 // Минимальная длина буфера
#define DIRECTORY 0 // Пиктограмма каталога
#define FILE 1 // Пиктограмма файла
//////////////////////////////////////////////////////////////
// Класс CFtpViewDlg
class CFtpViewDlg : public CDialog
{
// Construction
public:
~CFtpViewDlg();
CFtpViewDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CFtpViewDlg)
enum { IDD = IDD_FTPVIEW_DIALOG };
CEdit m_Status; // Поле Directory
CButton m_Ok; // Кнопка OK
CButton m_OnTop; // Кнопка On top
CButton m_Connect; // Кнопка Connect
CListCtrl m_FtpList; // Таблица с содержимым каталога
CString m_FtpAddress; // Поле адреса сервера FTP
//}}AFX_DATA
//{{AFX_VIRTUAL(CFtpViewDlg)
protected:
// Обмен данными - DDX/DDV
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
protected:
CString sCurentDirectory; // Текущий каталог
CImageList* m_ImageList; // Список изображений
CFtpConnection* m_FtpConnection; // Сервер FTP
CInternetSession* m_InternetSession; // Сеанс связи
HICON m_hIcon; // Пиктограмма
// приложения
protected:
// Метод, просматривающий содержимое каталога
BOOL DirectoryView();
// Методы обрабатывающие сообщения
//{{AFX_MSG(CFtpViewDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnOnTop();
afx_msg void OnDblclkFtpList(NMHDR* pNMHDR,
LRESULT* pResult);
afx_msg void OnConnect();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
¨ В листинге файла FtpViewDlg.h, в блоке AFX_MSG, описание метода OnDblclkFtpList разбито на две строки. Это сделано исключительно из-за ограничений, налагаемых типографией. Вы должны поместить данное описание на одной строке, в противном случае MFC ClassWizard будет выдавать сообщение об ошибке и вы не сможете воспользоваться его средствами для просмотра и изменения программного кода
Конструктор, деструктор, методы класса CFtpViewDlg, а также таблица сообщений определены в файле FtpViewDlg.cpp. Мы привели исходный текст файла FtpViewDlg.cpp в листинге 2.7. В соответствии с текстом этого файла вы должны доработать методы OnInitDialog, OnConnect, DirectoryView, OnDblclkFtpList, OnOnTop.
Листинг 2.7. Файл FtpViewDlg.cpp
#include "stdafx.h"
#include "FtpView.h"
#include "FtpViewDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//============================================================
// Конструктор класса CFtpViewDlg
//============================================================
CFtpViewDlg::CFtpViewDlg(CWnd* pParent /*=NULL*/)
: CDialog(CFtpViewDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CFtpViewDlg)
m_FtpAddress = _T("");
//}}AFX_DATA_INIT
// При использовании метода LoadIcon нет необходимости
// вызывать DestroyIcon
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
//============================================================
// Деструктор класса CFtpViewDlg
//============================================================
CFtpViewDlg::~CFtpViewDlg()
{
// Если соединение с сервером установлено, закрываем его
if (m_FtpConnection != NULL)
{
m_FtpConnection -> Close();
delete m_FtpConnection;
}
// Завершаем сеанс связи с Internet
if (m_InternetSession != NULL)
{
m_InternetSession -> Close();
delete m_InternetSession;
}
// Удаляем список изображений
delete m_ImageList;
}
//============================================================
// Метод DoDataExchange класса CFtpViewDlg
// Выполняет привязку органов управления диалоговой панели
// IDD_FTPVIEW_DIALOG и соответствующих элементов класса
// CFtpViewDlg
//============================================================
void CFtpViewDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CFtpViewDlg)
DDX_Control(pDX, IDC_STATUS, m_Status);
DDX_Control(pDX, IDOK, m_Ok);
DDX_Control(pDX, IDC_ON_TOP, m_OnTop);
DDX_Control(pDX, IDC_CONNECT, m_Connect);
DDX_Control(pDX, IDC_FTP_LIST, m_FtpList);
DDX_Text(pDX, IDC_FTP_ADDRESS, m_FtpAddress);
//}}AFX_DATA_MAP
}
//============================================================
// Таблица сообщений класса CFtpViewDlg
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewDlg, CDialog)
//{{AFX_MSG_MAP(CFtpViewDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
// Сообщение от кнопки Connect
ON_BN_CLICKED(IDC_CONNECT, OnConnect)
// Сообщение от кнопки On Top
ON_BN_CLICKED(IDC_ON_TOP, OnOnTop)
// Сообщение с кодом извещения NM_DBLCLK от списка
ON_NOTIFY(NM_DBLCLK, IDC_FTP_LIST, OnDblclkFtpList)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//============================================================
// Метод OnInitDialog класса CFtpViewDlg
// Выполняет инициализацию диалоговой панели, а также сеанса
// связи с Internet (создает объект класса CInternetSession)
//============================================================
BOOL CFtpViewDlg::OnInitDialog()
{
// Вызываем метод OnInitDialog базового класса CDialog
CDialog::OnInitDialog();
// Устанавливаем пиктограммы, которые будут отображаться
// в случае минимизации диалоговой панели
SetIcon(m_hIcon,TRUE); // Пиктограмма стандартного размера
SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера
//=========================================================
// Выполняем инициализацию списка IDC_FTP_LIST
//=========================================================
// Структура, для описания характеристик колонок списка
LV_COLUMN lv_column;
// Переменная для определения размера списка
CRect rectList;
// Названия для колонок списка
TCHAR szColHeader[COL_NUM][10] = {
_T("Name"),
_T("Length"),
_T("Date"),
_T("Time"),
};
// Определяем размер области, которую занимает список
// IDC_FTP_LIST в диалоговой панели
m_FtpList.GetWindowRect(&rectList);
// Указываем поля структуры lv_column, которые будут
// использоваться
lv_column.mask = LVCF_FMT | // Используется поле fmt
LVCF_SUBITEM | // Используется поле iSubItem
LVCF_TEXT | // Используется поле pszText
LVCF_WIDTH; // Используется поле cx
// Задаем выравнивание по левому краю
lv_column.fmt = LVCFMT_LEFT;
// Ширина колонки
lv_column.cx = (rectList.Width() / COL_NUM) - 1;
// Определяем характеристики колонок списка
for (int i = 0; i < COL_NUM; i++)
{
// Номер колонки
lv_column.iSubItem = i;
// Заголовок колонки
lv_column.pszText = szColHeader[i];
// Добавляем колонку с заданными свойствами к списку
m_FtpList.InsertColumn(i, &lv_column);
}
// Создаем список из двух изображений размера 16 х 16
m_ImageList = new CImageList();
m_ImageList -> Create(16, 16, TRUE, 2, 2);
// Добавляем в список две пиктограммы -
// IDI_DIRECTORY (изображение каталога) и
// IDI_FILE(изображение файла )
m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_DIRECTORY));
m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_FILE));
// Выбираем список изображений m_ImageList для
// использования в списке IDC_FTP_LIST
m_FtpList.SetImageList(m_ImageList, LVSIL_SMALL);
//=========================================================
// Выбираем адрес сервера FTP, который используется по
// умолчанию
//=========================================================
m_FtpAddress = "dials.ccas.ru"; // Сервер FTP “ДиалогНаука”
// Отображаем адрес на экране
UpdateData(FALSE);
//=========================================================
// Инициализируем сеанс связи с Internet
//=========================================================
// Создаем сеанс связи с Internet, указываем в качестве
// имени программы-клиента название приложения FtpView
m_InternetSession = new CInternetSession("FtpView");
// В случае ошибки отображаем сообщение и завершаем
// приложение
if(!m_InternetSession)
{
AfxMessageBox("New Session Error", MB_OK);
OnOK();
}
// Инициализируем указатель m_FtpConnection
m_FtpConnection = NULL;
return TRUE;
}
//============================================================
// Метод OnPaint класса CFtpViewDlg
// Отображает на экране пиктограмму в случае минимизации
// главной диалоговой панели приложения
//============================================================
void CFtpViewDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
SendMessage(WM_ICONERASEBKGND,
(WPARAM) dc.GetSafeHdc(), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//============================================================
// Метод OnQueryDragIcon класса CFtpViewDlg
// Сообщает идентификатор пиктограммы, отображаемой в случае
// минимизации приложения
//============================================================
HCURSOR CFtpViewDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
//============================================================
// Метод OnOnTop класса CFtpViewDlg
// Переходит в каталог верхнего уровня
//============================================================
void CFtpViewDlg::OnOnTop()
{
// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);
// Изменяем строку текущего каталога sCurentDirectory так,
// чтобы она показывала на каталог верхнего уровня.
// Ищем последнее вхождение символа / в строку с именем
// каталога
int iNum = sCurentDirectory.ReverseFind('/');
if(iNum == -1)
{
// Если символ / не обнаружен, значит мы находимся в
// корневом каталоге
AfxMessageBox("No top directory");
}
else
{
// Удаляем из строки с именем текущего каталога названия
// последнего каталога
sCurentDirectory = sCurentDirectory.Left(iNum);
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое каталога верхнего уровня
DirectoryView();
}
// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);
// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
return;
}
//============================================================
// Метод OnDblclkFtpList класса CFtpViewDlg
// Переходит в выбранный каталог
//============================================================
void CFtpViewDlg::OnDblclkFtpList(NMHDR* pNMHDR,
LRESULT* pResult)
{
int iTotalNumber; // Количество элементов списка
CString sSelItem; // Название каталога
CString sLength_Dir; // Длина файла или строка Dir
// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);
// Определяем количество элементов в списке IDC_FTP_LIST
iTotalNumber = m_FtpList.GetItemCount();
// Определяем, какой объект списка выбран
for(int i = 0; i < iTotalNumber; i++)
{
// Атрибут LVIS_SELECTED установлен
// у выбранного элемента списка
if(LVIS_SELECTED == m_FtpList.GetItemState( i,
LVIS_SELECTED ))
{
// Определяем название выбранного элемента списка
// (имя файла или каталога)
sSelItem = m_FtpList.GetItemText( i, 0 );
// Считываем данные из колонки Length
sLength_Dir = m_FtpList.GetItemText( i, 1 );
if(sLength_Dir == "Dir") // Выбран каталог
// Переходим в выбранный каталог
sCurentDirectory =
sCurentDirectory + "/" + sSelItem;
else // Выбран файл
// Отображаем имя файла
AfxMessageBox("You select file " + sSelItem);
break;
}
}
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое выбранного каталога
DirectoryView();
// Отображаем в диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);
*pResult = 0;
}
//============================================================
// Метод OnConnect класса CFtpViewDlg
// Соединяется с указанным сервером FTP
//============================================================
void CFtpViewDlg::OnConnect()
{
// Текущий каталог на сервере FTP
CString sCurrentFtpDirectory = "";
// Блокируем кнопки Connect, OK и On Top
m_Connect.EnableWindow(FALSE);
m_Ok.EnableWindow(FALSE);
m_OnTop.EnableWindow(FALSE);
// Если вы ранее уже соединились с сервером FTP, разрываем
// эту связь и удаляем объект m_FtpConnection
if (m_FtpConnection != NULL)
{
m_FtpConnection -> Close();
delete m_FtpConnection;
m_FtpConnection = NULL;
}
// Считываем из диалоговой панели адрес сервера FTP, так
// как пользователь уже мог его изменить
UpdateData(TRUE);
// Пытаемся соединиться с сервером FTP
try
{
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Соединяемся с сервером FTP. Эта операция
// может вызвать исключение CInternetException
m_FtpConnection =
m_InternetSession->GetFtpConnection(m_FtpAddress);
}
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
AfxMessageBox(szErr);
else
AfxMessageBox("GetFtpConnection Error");
// Удаляем исключение
pEx->Delete();
// Обнуляем указатель m_FtpConnection
m_FtpConnection = NULL;
}
// Если соединение не установлено сообщяем об этом
if( m_FtpConnection == NULL )
m_Status.SetWindowText("Connect not established");
// Если соединение установлено, определяем текущий каталог
// и отображаем его содержимое на экране
else
{
// Определяем текущий каталог сервера FTP
BOOL fResult=
m_FtpConnection ->
GetCurrentDirectory(sCurrentFtpDirectory);
if(fResult)
sCurentDirectory = sCurrentFtpDirectory;
else
AfxMessageBox("GetCurrentDirectory Error");
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое выбранного каталога
DirectoryView();
// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
}
// Снимаем блокировку кнопок Connect, OK и On Top
m_Connect.EnableWindow(TRUE);
m_Ok.EnableWindow(TRUE);
m_OnTop.EnableWindow(TRUE);
}
//============================================================
// Метод DirectoryView класса CFtpViewDlg
// Просматривает содержимое каталога и отображает его в
// таблице m_FtpList
//============================================================
BOOL CFtpViewDlg::DirectoryView()
{
// Переменная, сигнализирующая о получении последнего
// элемента каталога
BOOL fResult;
// Временная переменная, определяющая тип объекта -
// файл или каталог
BOOL fDirectory;
// Структура для добавления нового элемента к списку
LV_ITEM lv_item;
// Удалить все элементы из списка IDC_FTP_LIST
m_FtpList.DeleteAllItems();
// Создаем объект класса CFtpFileFind, указывая
// объект, представляющий уже установленное соединение
// с сервером FTP
CFtpFileFind m_FtpFileFind(m_FtpConnection);
// Получаем имена всех объектов текущего каталога
if(fResult =
m_FtpFileFind.FindFile(_T(sCurentDirectory + "/*")))
{
for(int n = 0;fResult; n++)
{
// Получаем очередной объект из каталога
fResult = m_FtpFileFind.FindNextFile();
// Определяем что это - каталог или файл
fDirectory = m_FtpFileFind.IsDirectory();
//============= Определяем имя объекта ==============
// Временные строка для имени каталога или файла
CString fileName;
// Определяем имя объекта
fileName = m_FtpFileFind.GetFileName();
// Заполняем структуру lv_item, сведениями об
// очередном объекте каталога сервера FTP. Указываем,
// что в список добавляется текст и изображение
lv_item.mask = LVIF_TEXT | LVIF_IMAGE;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем первую колонку
lv_item.iSubItem = 0;
// Выбираем изображение для нового элемента списка в
// зависимости от типа объекта
lv_item.iImage = (fDirectory) ? DIRECTORY : FILE;
// Указываем имя каталога или файла
lv_item.pszText = fileName.GetBuffer(MIN_LEN_BUF);
// Добавляем новый элемент к списку IDC_FTP_LIST
m_FtpList.InsertItem(&lv_item);
//============= Определяем длину файла =============
// Длинна файла
DWORD dwLength = 0;
// Временные строка для формирования текстового
// представления длинны файла
CString sLength;
// Заполняем колонку Length для новой записи и
// записываем в нее длинну файла или строку Dir, если
// новый объект - каталог.
// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем вторую колонку
lv_item.iSubItem = 1;
// Если очередной объект является каталогом, то
// вместо в колонке Length записываем строку Dir
if(fDirectory)
{
lv_item.pszText = "Dir";
}
// Если очередной объект является файлом, то
// записываем его длину в колонку Length
else
{
// Определяем длину файла
dwLength = m_FtpFileFind.GetLength();
// Формируем текстовое представление длины файла
sLength.Format("%d", dwLength);
lv_item.pszText = sLength.GetBuffer(MIN_LEN_BUF);
}
// Добавляем запись во второй колонке списка (колонка
// Length)
m_FtpList.SetItem(&lv_item);
//======== Определяем дату и время создания =========
// Дата и время создания каталога или файла
CTime mTime;
// Временные строки для формирования текстового
// представления даты и времени
CString sDate;
CString sTime;
// Определяем время изменения файла или каталога
if(!m_FtpFileFind.GetLastWriteTime(mTime))
break;
// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем третью колонку
lv_item.iSubItem = 2;
// Выделяем из объекта mTime день, месяц и год
sDate = mTime.Format("%d.%m.%y");
// Записываем сформированную дату в структуру lv_item
lv_item.pszText = sDate.GetBuffer(MIN_LEN_BUF);
// Добавляем запись во второй колонке списка (колонка
// Date)
m_FtpList.SetItem(&lv_item);
// Заполняем четвертую колонку, записываем в нее
// время последнего изменения файла (каталога)
lv_item.iSubItem = 3;
// Выделяем из объекта mTime часы, минуты и секунды
sTime = mTime.Format("%H:%M:%S");
// Записываем сформированную строку, содержащую время
lv_item.pszText = sTime.GetBuffer(MIN_LEN_BUF);
// Добавляем запись во второй колонке списка (колонка
// Time)
m_FtpList.SetItem(&lv_item);
}
// Заканчиваем поиск объектов в каталоге, закрываем
// объект m_FtpFileFind
m_FtpFileFind.Close();
}
// Если каталог не содержит других объектов - каталогов и
// файлов, выводин соответствующее сообщение
else
AfxMessageBox("File's not found or error");
return TRUE;
}
Кроме уже описанных нами файлов в проект FtpView входят еще два исходных файла, содержащих программный код. Это файлы stdafx.cpp и включаемый файл stdafx.h. Исходный текст файла stdafx.cpp содержится в листинге 2.8. Как видите, он состоит из единственной директивы #include, включающей файл stdafx.h.
Листинг 2.8. Файл stdafx.cpp
// Включаем файл stdafx.h, определенный в нашем приложении
#include “stdafx.h“
Файл stdafx.h задействует часто используемые включаемые системные файлы - afxwin.h, afxext.h, afxcmn.h и afxinet.h. Эти файлы не изменяются. Поэтому Microsoft Visual C++ компилирует их только один раз. За счет этого значительно сокращается время, затрачиваемое на повторное построение проекта.
Последний включаемый файл afxinet.h содержит описание классов, структур и констант библиотеки MFC, относящихся к программному интерфейсу WinInet (листинг 2.9). MFC AppWizard не включает этот файл по умолчанию. Вы должны добавить соответствующую директиву #include самостоятельно.
#include "stdafx.h"
Листинг 2.9. Файл stdafx.h
// Исключает редко используемые определения при обработке
// файлов заголовков
#define VC_EXTRALEAN
// Основные компоненты библиотеки MFC
#include
// Расширения MFC
#include
#ifndef _AFX_NO_AFXCMN_SUPPORT
// Используется для органов управления Windows
#include
#endif // _AFX_NO_AFXCMN_SUPPORT
// Включаемый файл для библиотеки WinInet.
// MFC AppWizard не включает этот файл, вы должны сделать это
// самостоятельно
#include "afxinet.h"
Класс CGopherConnection
Класс CGopherConnection управляет соединением с сервером Gopher. Класс CGopherConnection наследуется от базового класса CInternetConnection:CObject -> CInternetConnection -> CGopherConnection
В состав класса CGopherConnection входят конструктор и три метода - OpenFile, CreateLocator, GetAttribute. Метод OpenFile открывает файл на сервере Gopher. С помощью метода CreateLocator можно выполнить поиск файлов на сервере Gopher, а метод GetAttribute позволяет получить различную информацию о объекте на сервере.
С классом CGopherConnection непосредственно связаны еще два класса - CGopherLocator и CGopherFileFind. Эти классы предназначены для работы с серверами Gopher.
Серверы Gopher, также как и сервера WWW, предоставляют доступ к гипертекстовой информации. Однако они имеют значительно меньше возможностей и таких серверов постепенно становится все меньше и меньше. Существует мнение, что серверы Gopher остались только в тех организациях, которые не в состоянии оплатить создание сервера WWW и перенос на него всей накопленной информации. Поэтому мы не будем подробно останавливаться на соответствующих классах MFC. Дополнительную информацию о них вы можете получить из документации Microsoft Visual C++, а примеры программ в книге Special Edition Using Visual C++ 4.2.
Класс CGopherFile
Также как класс CHttpFile, класс CGopherFile наследуется от базового класса CInternetFile. Вместе с другими классами WinInet он позволяет организовать поиск и получение файлов с серверов Gopher.В класс CGopherFile входит конструктор CGopherFile и метод Close, который закрывает соединение с сервером Gopher.
Класс CGopherFileFind
Класс CGopherFileFind наследуется от базового класса CFileFind и используется для поиска файлов на серверах Gopher. В следующей таблице мы привели описание различных методов класса CGopherFileFind:| Метод | Описание | ||
| CGopherFileFind | Конструктор класса CGopherFileFind. Создает объект класса CGopherFileFind | ||
| FindFile | Начинает поиск файла или файлов на сервере Gopher | ||
| FindNextFile | Продолжает поиск файла или файлов в указанном каталоге сервера Gopher. Вы должны использовать этот метод после обращения к методу FindFile | ||
| GetLocator | Определяет объект CGopherLocator | ||
| GetScreenName | Определяет имя экрана Gopher | ||
| GetLength | Определяет длину файла в байтах |
Класс CGopherLocator
Класс CGopherLocator наследуется непосредственно от базового класса CObject. Этот класс предназначен для доступа к серверам Gopher.CObject -> CGopherLocator
В состав метода CGopherLocator входит конструктор класса, метод GetLocatorType и оператор LPCTSTR.
Метод GetLocatorType разбирает локатор (gopher locator) и определяет его атрибуты, а оператор LPCTSTR позволяет получить прямой доступ к данным, записанным в объекте CGopherLocator как к обычной строке символов.
Класс CHttpConnection
Класс CHttpConnection позволяет приложению взаимодействовать с сервером WWW. Этот класс наследуется от базового класса CInternetConnection:CObject -> CInternetConnection -> CHttpConnection
Кроме конструктора в состав класса CHttpConnection входит только один метод OpenRequest, который устанавливает соединение с сервером HTTP.
Конструктор класса CHttpConnection никогда не вызывается напрямую. Объект класса CHttpConnection создается при вызове метода GetHttpConnection класса CInternetSession. Поэтому мы опустим описание конструктора класса и сразу перейдем к методу OpenRequest.
Класс CHttpFile
Класс CHttpFile наследуется от базового класса CInternetFile. Он позволяет работать с сервером WWW по протоколу HTTP. В следующей таблице мы представили описание методов класса CHttpFile:| Метод | Описание | ||
| CHttpFile | Конструктор класса. Создает объект CHttpFile | ||
| AddRequestHeaders | Добавляет заголовок к запросу HTTP, передаваемому на сервер WWW | ||
| SendRequest | Передает запрос HTTP на сервер WWW | ||
| QueryInfo | Получает ответ или заголовок запроса от сервера WWW | ||
| QueryInfoStatusCode | Определяет код состояния, связанный с данным запросом HTTP | ||
| GetVerb | Определяет действие, которое было указано в запросе, переданном на сервер WWW | ||
| GetObject | Определяет объект, для которого был выполнен запрос на сервер WWW | ||
| GetFileURL | Позволяет получить имя файла на сервере WWW в формате URL | ||
| Close | Закрывает объект CHttpFile и освобождает все используемые им ресурсы |
Класс CInternetConnection
Класс CInternetConnection управляет наиболее общими характеристиками соединения с серверами Internet. Если приложению требуется использовать расширенный сервис серверов FTP, HTTP и системы Gopher, воспользуйтесь классами CFtpConnection, CHttpConnection и CGopherConnection. Эти классы наследуются от базового класса CInternetConnection, добавляя к нему набор специфических методов. Сам класс CInternetConnection наследуется от базового класса CObject:CObject -> CInternetConnection -> CFtpConnection
|
-> CHttpConnection
|
-> CGopherConnection
В состав класса CInternetConnection, кроме конструктора, входят три метода GetContext, GetSession, GetServerName и оператор HINTERNET.
Все методы класса CInternetConnection предназначены для определения параметров соединения с сервером. Метод GetContext позволяет узнать идентификатор контекста объекта, представляющего соединение. Метод GetSession определяет объект класса CInternetSession, в рамках которого установлено соединение с сервером. Метод GetServerName возвращает имя сервера.
Оператор HINTERNET не представляет особого интереса и позволяет узнать идентификатор сеанса связи с Internet. Он используется, если в вашем приложении одновременно применяются классы MFC, управляющие WinInet и функции программного интерфейса WinInet.
Мы не будем напрямую вызывать методы класса CInternetConnection в своих приложениях и поэтому не станем останавливаться на них более подробно и рассмотрим только оператор HINTERNET, так как он входит во многие другие классы WinInet.
Класс CInternetException
Для обработки ошибок, возникающих при работе с WinInet, в состав библиотеки MFC включен новый класс CInternetException. Точно также как и другие классы MFC, предназначенные для обработки различных исключительных ситуаций, класс CInternetException наследован от базового класса CException:CObject -> CException -> CInternetException
Класс CInternetFile
В 28 томе, посвященном библиотеке MFC мы описывали классы CFile и CStdioFile, предназначенные для работы с файловой системой компьютера. Среди классов WinInet, входящих в библиотеку MFC, существует класс CInternetFile, наследованный от базового класса CStdioFile. В этом классе определены только самые общие методы для работы с серверами Internet.От класса CInternetFile наследуются еще два класса WinInet - CGopherFile и CHttpFile. Эти классы ориентированны на работу, соответственно, с серверами Gopherf и HTTP:
CObject -> CFile -> CStdioFile -> CInternetFile -> CGopherFile
|
-> CHttpFile
Краткое описание методов, входящих в состав класса CInternetFile мы представили в следующей таблице:
| Метод | Описание | ||
| CInternetFile | Конструктор класса CInternetFile | ||
| Abort | Закрывает файл на сервере, не принимая во внимание все возможные предупреждения и ошибки | ||
| Close | Закрывает файл CInternetFile и освобождает используемые им ресурсы | ||
| Flush | При записи данных на сервер классы WinInet выполняют их буферизацию. Метод Flush принудительно передает данные из буфера на сервер | ||
| HINTERNET | Возвращает идентификатор текущего сеанса связи с Internet | ||
| Read | Считывает данные с сервера | ||
| ReadString | Считывает строку символов из файла на сервере | ||
| Seek | Перемещает указатель в открытом файле на сервере | ||
| SetReadBufferSize | Устанавливает размер буфера для данных, которые загружаются с сервера | ||
| SetWriteBufferSize | Устанавливает размер буфера для данных, которые записываются на сервер | ||
| Write | Записывает данные на сервер | ||
| WriteString | Записывает в файл на сервере строку символов, закрытую нулем |
Кроме перечисленных методов в состав класса CInternetFile входит элемент данных m_hFile, содержащий идентификатор файла.
Класс CInternetSession
Класс CInternetSession представляет сеанс. Вы должны создать объект класса CInternetSession перед использованием остальных классов и методов WinInet. Вы можете либо непосредственно создать объект класса CInternetSession, либо сначала наседовать от класса CInternetSession собственный клас и использовать уже его.Класс CInternetSession наследуется от базового класса CObject, лежащего в основе большинства классов библиотеки MFC:
CObject -> CInternetSession
Кроме конструктора, в состав класса CInternetSession входят несколько методов, выполняющих разнообразные действия. Они позволяют определить и изменить характеристиками данного сеанса связи, открыть для чтения объект на указанном сервере Internet, установить соединение с серверами FTP, WWW и Gopher и т. д. В следующей таблице мы привели список этих методов:
| Метод | Описание | ||
| CInternetSession | Конструктор класса CInternetSession | ||
| Close | Закрывает соединение с Internet. Вы должны вызвать этот метод после того, как вы закончите использовать объект CInternetSession | ||
| EnableStatusCallback | Управляет работой функции обратного вызова WinInet. Эта функция реализована как метод OnStatusCallback класса CInternetSession | ||
| GetContext | Определяет значение идентификатора контекста для данного сеанса связи | ||
| GetFtpConnection | Устанавливает сеанс связи с сервером FTP | ||
| GetGopherConnection | Устанавливает сеанс связи с сервером Gopher | ||
| GetHttpConnection | Устанавливает сеанс связи с сервером WWW | ||
| HINTERNET | Возвращает идентификатор текущего сеанса связи с Internet | ||
| OnStatusCallback | Данный метод вызывается когда изменяется состояние сеанса связи. Чтобы разрешить использование этой функции необходимо воспользоваться методом EnableStatusCallback | ||
| OpenURL | Открывает объект, расположенный по заданному адресу URL, для загрузки в локальный компьютер | ||
| QueryOption | Запрашивает различные характеристики соединения с Internet. Например время, по истечении которого, запрос отменяется в случае если на него не получен ответ, размеры буферов, используемых для чтения и записи и т. д. Вы можете менять все эти характеристики при помощи метода SetOption, также входящего в класс CInternetSession | ||
| ServiceTypeFromHandle | Определить тип сервиса (ftp, http, gopher, file), соответствующий данному идентификатору | ||
| SetOption | Устанавливает различные параметры сеанса связи с Internet. Чтобы узнать значение текущих параметров связи следует воспользоваться методом QueryOption |
Рассмотрим более подробно конструктор класса и те его методы, которые будут нами использоваться в дальнейшем.
Класс CLookApp
Главный класс приложения CLookApp, наследованный от базового класса CWinApp, определен во включаемом файле Look.h. Исходный текст этого файла представлен в листинге 4.1. Фактически в классе CLookApp определен только конструктор класса и переопределен метод InitInstance базового класса CWinApp.Листинг 4.1. Файл Look.h
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h"
//////////////////////////////////////////////////////////////
// Определение класса CLookApp
//
class CLookApp : public CWinApp
{
public:
CLookApp();
// Overrides
//{{AFX_VIRTUAL(CLookApp)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CLookApp)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//////////////////////////////////////////////////////////////
Реализация класса CLookApp содержится в файле Look.cpp (листинг 4.2). В нем определены конструктор класса CLookApp, метод InitInstance и таблица сообщений класса CLookApp. Кроме того, в файле Look.cpp объявлен глобальный объект theApp главного класса приложения CLookApp.
Листинг 4.2. Файл Look.cpp
#include "stdafx.h"
#include "Look.h"
#include "LookDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CLookApp
BEGIN_MESSAGE_MAP(CLookApp, CWinApp)
//{{AFX_MSG_MAP(CLookApp)
// DO NOT EDIT
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////
// Конструктор класса CLookApp
CLookApp::CLookApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
//////////////////////////////////////////////////////////////
// Объект глаавного класса приложения
CLookApp theApp;
//////////////////////////////////////////////////////////////
// Метод InitInstance класса CLookApp.
// Выполняет инициализацию приложения
BOOL CLookApp::InitInstance()
{
// Разрешаем использование органов управления ActiveX
AfxEnableControlContainer();
// Выполняем стандартную инициализацию
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
// Отображаем на экране диалоговую панель приложения
CLookDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Нажата клавиша OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Нажата клавиша Cancel
}
return FALSE;
}
Класс CLookDlg и органы управления панели IDD_LOOK_DIALOG
Чтобы нам было удобно работать с органами управления диалоговой панели IDD_LOOK_DIALOG, запустите MFC ClassWizard и привяжите к ним переменные, добавив их к классу CLookDlg. К полям редактирования IDC_COMBO_ADDRESS, IDC_TITLE_BAR и IDC_STATUS_TEXT привяжите строки m_address, m_TitleBar и m_StatusText. Эти переменные будут использоваться нами, чтобы узнать адрес сервера, выбранный из списка IDC_COMBO_ADDRESS и вывести в полях IDC_TITLE_BAR и IDC_STATUS_TEXT заголовок страницы и строку состояния.К линейному индикатору IDC_PROGRESS привяжите объект m_progress. Обратите внимание, что это управляющий объект класса CProgressCtrl и чтобы изменять его состояние мы будем пользоваться методами этого класса.
И, наконец, самое главное. К навигатору IDC_EXPLORER вы должны привязать управляющий объект m_explorer. Для него надо выбрать класс CWebBrowser. Напомним, что этот класс был добавлен в наш проект, когда мы вставили в него орган управления Microsoft Web Browser Control. Этот класс содержит большое количество методов, позволяющих управлять навигатором - переходить к просмотру заданных страниц, обновлять отображаемую информацию и т. д.

Рис. 4.10. Диалоговая панель IDD_LOOK_DIALOG и MFC ClassWizard
Внешний вид диалоговой панели MFC ClassWizard после того как мы добавили ко всем органам управления панели IDD_LOOK_DIALOG управляющие объекты (кроме кнопок) представлен на рисунке 4.10.
Класс CLookDlg
Основную работу по управлению диалоговой панелью приложения выполняет класс CLookDlg. Именно в него мы добавляли элементы, представляющие органы управления и методы, обрабатывающие сообщения от диалоговой панели.Класс CWebBrowser
Определение класса CWebBrowser расположено в файле webbrowser.h, исходный текст которого представлен в листинге 4.1. Методы GetClsid и Create определены непосредственно в самом классе CWebBrowser. Остальные методы класса CWebBrowser определены в файле webbrowser.cpp, содержащимся в листинге 4.3. Мы оставили комментарии на английском языке, которые были добавлены во время вставки Microsoft Web Browser Control в проект.Листинг 4.3. Файл webbrowser.h
#ifndef __WEBBROWSER_H__
#define __WEBBROWSER_H__
// Machine generated IDispatch wrapper class(es) created by
// Microsoft Visual C++
// NOTE: Do not modify the contents of this file. If this
// class is regenerated by Microsoft Visual C++, your
// modifications will be overwritten.
//////////////////////////////////////////////////////////////
// CWebBrowser wrapper class
class CWebBrowser : public CWnd
{
protected:
DECLARE_DYNCREATE(CWebBrowser)
public:
CLSID const& GetClsid()
{
static CLSID const clsid
= { 0xeab22ac3, 0x30c1, 0x11cf,
{ 0xa7, 0xeb, 0x0, 0x0, 0xc0, 0x5b, 0xae, 0xb }
};
return clsid;
}
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL)
{
return CreateControl(
GetClsid(),
lpszWindowName,
dwStyle,
rect,
pParentWnd,
nID);
}
BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID,
CFile* pPersist = NULL, BOOL bStorage = FALSE,
BSTR bstrLicKey = NULL)
{
return CreateControl(
GetClsid(),
lpszWindowName,
dwStyle,
rect,
pParentWnd,
nID,
pPersist,
bStorage,
bstrLicKey);
}
// Operations
public:
яя // method 'QueryInterface' not emitted because of invalid
яя // return type or parameter type
яя unsigned long AddRef();
яя unsigned long Release();
яя // method 'GetTypeInfoCount' not emitted because of invalid
яя // return type or parameter type
яя // method 'GetTypeInfo' not emitted because of invalid
яя // return type or parameter type
яя // method 'GetIDsOfNames' not emitted because of invalid
яя // return type or parameter type
яя // method 'Invoke' not emitted because of invalid return
яя // type or parameter type
яя void GoBack();
яя void GoForward();
яя void GoHome();
яя void GoSearch();
яя void Navigate(
яяяяя LPCTSTR URL, VARIANT* Flags, VARIANT* TargetFrameName,
яяяяя VARIANT* PostData, VARIANT* Headers);
яя void Refresh();
яя void Refresh2(VARIANT* Level);
яя void Stop();
яя LPDISPATCH GetApplication();
яя LPDISPATCH GetParent();
яя LPDISPATCH GetContainer();
яя LPDISPATCH GetDocument();
яя BOOL GetTopLevelContainer();
яя CString GetType();
яя long GetLeft();
яя void SetLeft(long nNewValue);
яя long GetTop();
яя void SetTop(long nNewValue);
яя long GetWidth();
яя void SetWidth(long nNewValue);
яя long GetHeight();
яя void SetHeight(long nNewValue);
яя CString GetLocationName();
яя CString GetLocationURL();
яя BOOL GetBusy();
};
#endif // __WEBBROWSER_H__
ЌҐ ४®¬Ґ¤гҐвбп ў®бЁвм Ё§¬ҐҐЁп ў Ёбе®¤лҐ вҐЄбвл Є« бб CWebBrowser Ё ⥪бвл ҐЈ® ¬Ґв®¤®ў, а бЇ®«®¦ҐлҐ ў д ©« е webbrowser.h Ё webbrowser.cpp. ќвЁ д ©«л ¬®Јгв Ўлвм б®§¤ л Microsoft Visual C++ § ®ў®, Ґб«Ё ўл Ї®ўв®а® ўбв ўЁвҐ ®аЈ гЇа ў«ҐЁп Microsoft Web Browser ў Їа®ҐЄв. ‚ н⮬ б«гз Ґ ўбҐ Ё§¬ҐҐЁп, ўҐбҐлҐ ў ¬Ё ў д ©«л webbrowser.h Ё webbrowser.cpp Ўг¤гв гЁз⮦Ґл.
…б«Ё г ў б ўбҐ ¦Ґ ў®§ЁЄҐв Ґ®Ўе®¤Ё¬®бвм ¬®¤ЁдЁжЁа®ў вм Є« бб CWebBrowser, б®§¤ ©вҐ ®ўл© Є« бб, Ї®а®¦¤Ґл© ®в Є« бб CWebBrowser Ё ЁбЇ®«м§г©вҐ ҐЈ®.
¨ Љ®Ј¤ ўл ¤®Ў ў«пҐвҐ ў Їа®ҐЄв ®аЈ гЇа ў«ҐЁп Microsoft Web Browser Control, ў д ©«Ґ ®ЇаҐ¤Ґ«ҐЁп Є« бб ®в¬Ґз Ґвбп, зв® ап¤ ¬Ґв®¤®ў (QueryInterface, AddRef, Release, GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, Invoke) Ґ Ї®¤Є«озҐл, в Є Є Є ®Ё Ё¬Ґов ҐЇа ўЁ«мл© вЁЇ ў®§ўа й Ґ¬®Ј® § зҐЁп Ё«Ё ҐЇа ўЁ«мл© вЁЇ Ї а ¬Ґва®ў. ‚ б«гз Ґ Ґб«Ё ўл Ї®«м§гҐвҐбм Microsoft Visual C++ ўҐабЁЁ 5.0 нвЁ б®®ЎйҐЁп Ґ Ї®пў«повбп, ® ЇҐаҐзЁб«ҐлҐ ¬Ґв®¤л в Є¦Ґ Ґ Ўг¤гв ¤®бвгЇл
‚ᥠ¬Ґв®¤л Є« бб CWebBrowser, § ЁбЄ«о票Ґ¬ ¬Ґв®¤ GetClsid Ё ¤ўге ¬®¤ЁдЁЄ жЁ© ¬Ґв®¤ Create, ®ЇаҐ¤Ґ«Ґл ў д ©«Ґ webbrowser.cpp. €бе®¤л© вҐЄбв нв®Ј® д ©« ЇаҐ¤бв ў«Ґ ў «ЁбвЁЈҐ 4.4. ЊҐв®¤л GetClsid Ё Create ®ЇаҐ¤Ґ«Ґл ҐЇ®б।б⢥® ў ®ЇЁб ЁЁ Є« бб CWebBrowser («ЁбвЁЈ 4.3).
‹ЁбвЁЈ 4.4. ” ©« webbrowser.cpp
// Machine generated IDispatch wrapper class(es) created by
// Microsoft Visual C++
// NOTE: Do not modify the contents of this file.я If this
// class is regenerated by Microsoft Visual C++, your
// modifications will be overwritten.
#include "stdafx.h"
#include "webbrowser.h"
//////////////////////////////////////////////////////////////
// CWebBrowser
IMPLEMENT_DYNCREATE(CWebBrowser, CWnd)
//////////////////////////////////////////////////////////////
// CWebBrowser properties
//////////////////////////////////////////////////////////////
// CWebBrowser operations
unsigned long CWebBrowser::AddRef()
{
яя unsigned long result;
яя InvokeHelper(0x60000001, DISPATCH_METHOD, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
unsigned long CWebBrowser::Release()
{
яя unsigned long result;
яя InvokeHelper(0x60000002, DISPATCH_METHOD, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
void CWebBrowser::GoBack()
{
яя InvokeHelper(0x64, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void CWebBrowser::GoForward()
{
яя InvokeHelper(0x65, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void CWebBrowser::GoHome()
{
яя InvokeHelper(0x66, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void CWebBrowser::GoSearch()
{
яя InvokeHelper(0x67, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
void CWebBrowser::Navigate(
яя LPCTSTR URL,
яя VARIANT* Flags,
яя VARIANT* TargetFrameName,
яя VARIANT* PostData,
яя VARIANT* Headers)
{
яя static BYTE parms[] =
яяяяя VTS_BSTR VTS_PVARIANT VTS_PVARIANT
яяяяя VTS_PVARIANT VTS_PVARIANT;
яя InvokeHelper(0x68, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
яяяяяя URL, Flags, TargetFrameName, PostData, Headers);
}
void CWebBrowser::Refresh()
{
яя InvokeHelper(DISPID_REFRESH, DISPATCH_METHOD, VT_EMPTY,
яяяяя NULL, NULL);
}
void CWebBrowser::Refresh2(VARIANT* Level)
{
яя static BYTE parms[] = VTS_PVARIANT;
яя InvokeHelper(0x69, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
яяяяя Level);
}
void CWebBrowser::Stop()
{
яя InvokeHelper(0x6a, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
LPDISPATCH CWebBrowser::GetApplication()
{
яя LPDISPATCH result;
яя InvokeHelper(0xc8, DISPATCH_PROPERTYGET, VT_DISPATCH,
яяяяя (void*)&result, NULL);
яя return result;
}
LPDISPATCH CWebBrowser::GetParent()
{
яя LPDISPATCH result;
яя InvokeHelper(0xc9, DISPATCH_PROPERTYGET, VT_DISPATCH,
яяяяя (void*)&result, NULL);
яя return result;
}
LPDISPATCH CWebBrowser::GetContainer()
{
яя LPDISPATCH result;
яя InvokeHelper(0xca, DISPATCH_PROPERTYGET, VT_DISPATCH,
яяяяя (void*)&result, NULL);
яя return result;
}
LPDISPATCH CWebBrowser::GetDocument()
{
яя LPDISPATCH result;
яя InvokeHelper(0xcb, DISPATCH_PROPERTYGET, VT_DISPATCH,
яяяяя (void*)&result, NULL);
яя return result;
}
BOOL CWebBrowser::GetTopLevelContainer()
{
яя BOOL result;
яя InvokeHelper(0xcc, DISPATCH_PROPERTYGET, VT_BOOL,
яяяяя (void*)&result, NULL);
яя return result;
}
CString CWebBrowser::GetType()
{
яя CString result;
яя InvokeHelper(0xcd, DISPATCH_PROPERTYGET, VT_BSTR,
яяяяя (void*)&result, NULL);
яя return result;
}
long CWebBrowser::GetLeft()
{
яя long result;
яя InvokeHelper(0xce, DISPATCH_PROPERTYGET, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
void CWebBrowser::SetLeft(long nNewValue)
{
яя static BYTE parms[] = VTS_I4;
яя InvokeHelper(0xce, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
яяяяя parms, nNewValue);
}
long CWebBrowser::GetTop()
{
яя long result;
яя InvokeHelper(0xcf, DISPATCH_PROPERTYGET, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
void CWebBrowser::SetTop(long nNewValue)
{
яя static BYTE parms[] = VTS_I4;
яя InvokeHelper(0xcf, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
яяяяя parms, nNewValue);
}
long CWebBrowser::GetWidth()
{
яя long result;
яя InvokeHelper(0xd0, DISPATCH_PROPERTYGET, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
void CWebBrowser::SetWidth(long nNewValue)
{
яя static BYTE parms[] = VTS_I4;
яя InvokeHelper(0xd0, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
яя parms, nNewValue);
}
long CWebBrowser::GetHeight()
{
яя long result;
яя InvokeHelper(0xd1, DISPATCH_PROPERTYGET, VT_I4,
яяяяя (void*)&result, NULL);
яя return result;
}
void CWebBrowser::SetHeight(long nNewValue)
{
яя static BYTE parms[] = VTS_I4;
яя InvokeHelper(0xd1, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
яя parms, nNewValue);
}
CString CWebBrowser::GetLocationName()
{
яя CString result;
яя InvokeHelper(0xd2, DISPATCH_PROPERTYGET, VT_BSTR,
яяяяя (void*)&result, NULL);
яя return result;
}
CString CWebBrowser::GetLocationURL()
{
яя CString result;
яя InvokeHelper(0xd3, DISPATCH_PROPERTYGET, VT_BSTR,
яяяяя (void*)&result, NULL);
яя return result;
}
BOOL CWebBrowser::GetBusy()
{
яя BOOL result;
яя InvokeHelper(0xd4, DISPATCH_PROPERTYGET, VT_BOOL,
яяяяя (void*)&result, NULL);
яя return result;
}
‚ᥠ¬Ґв®¤л, ®ЇаҐ¤Ґ«ҐлҐ ў д ©«Ґ webbrowser.cpp, ўл§лў ов ўбЇ®¬®Ј ⥫мл© ¬Ґв®¤ InvokeHelper Є« бб CWnd, ЇҐаҐ¤ ў п Ґ¬г а §«ЁзлҐ Ї а ¬Ґвал. ‚ § ўЁбЁ¬®бвЁ ®в бў®Ёе Ї а ¬Ґва®ў, ¬Ґв®¤ InvokeHelper, ў бў®о ®зҐаҐ¤м, ўл§лў Ґв ⥠Ё«Ё ЁлҐ ¬Ґв®¤л ®аЈ гЇа ў«ҐЁп ActiveX. ‚ 襬 б«гз Ґ ў Є зҐб⢥ нв®Ј® ®аЈ гЇа ў«ҐЁп ўлбвгЇ Ґв Microsoft Web Browser Control.
Џ®¤а®Ў®Ґ ®ЇЁб ЁҐ Microsoft Web Browser Control, ўЄ«оз п ®ЇЁб ЁҐ гЇа ў«по饣® Є« бб , ᬮваЁвҐ бва ЁжҐ http://www.microsoft.com бҐаўҐа WWW Є®¬Ї ЁЁ Microsoft. ‚ б«Ґ¤го饩 в Ў«ЁжҐ ¬л ¤ ¤Ё¬ Єа вЄ®Ґ ®ЇЁб ЁҐ ®б®ўле ¬Ґв®¤®ў Є« бб CWebBrowser:
|
ЊҐв®¤ |
ЋЇЁб ЁҐ |
|
GetBusy |
Џ®§ў®«пҐв г§ вм, ўлЇ®«пҐв «Ё ®аЈ гЇа ў«ҐЁп Microsoft Web Browser Control ў ¤ л© ¬®¬Ґв ўаҐ¬ҐЁ § Јаг§Єг ¤ ле Ё§ Internet |
|
GetHeight |
ЋЇаҐ¤Ґ«пҐв ўҐавЁЄ «мл© а §¬Ґа ®Є (frame window), ᮤҐа¦ 饣® ®аЈ гЇа ў«ҐЁп Web Browser |
|
GetLeft |
ЋЇаҐ¤Ґ«пҐв а ббв®пЁҐ ¬Ґ¦¤г ўгв॥© «Ґў®© бв®а®®© ®аЈ гЇа ў«ҐЁп Web Browser Ё «Ґў®© бв®а®®© ҐЈ® Є®вҐ©Ґа (ᮤҐа¦ 饣® ҐЈ® ®Є ) |
|
GetLocationName |
ЋЇаҐ¤Ґ«пҐв Ё¬п аҐбгаб Internet, Є®в®ал© ®в®Ўа ¦ Ґвбп ®аЈ ®¬ гЇа ў«ҐЁп Microsoft Web Browser Control |
|
GetLocationURL |
ЋЇаҐ¤Ґ«пҐв URL Ё¬п аҐбгаб Internet, Є®в®ал© ®в®Ўа ¦ Ґвбп ®аЈ ®¬ гЇа ў«ҐЁп Microsoft Web Browser Control |
|
GetTop |
ЋЇаҐ¤Ґ«пҐв а ббв®пЁҐ ¬Ґ¦¤г ўгв॥© ўҐа奩 бв®а®®© ®аЈ гЇа ў«ҐЁп Web Browser Ё ўҐа奩 бв®а®®© ҐЈ® Є®вҐ©Ґа |
|
GetWidth |
ЋЇаҐ¤Ґ«пҐв Ј®аЁ§®в «мл© а §¬Ґа ®Є (frame window), ᮤҐа¦ 饣® ®аЈ гЇа ў«ҐЁп Web Browser |
|
GoBack |
‚Ґагвмбп Є Їа®б¬®ва㠯।л¤г饩 бва Ёжл WWW |
|
GoForward |
ЏҐаҐ©вЁ Є Їа®б¬®ваг б«Ґ¤го饩 бва Ёжл WWW. ќв®в ¬Ґв®¤ ¬®¦® ЁбЇ®«м§®ў вм, Ґб«Ё ўл ўҐаг«Ёбм Є Їа®б¬®ва㠯।л¤г饩 бва Ёжл WWW б Ї®¬®ймо ¬Ґв®¤ GoBack |
|
GoHome |
ЏҐаҐ©вЁ Є Їа®б¬®ваг ¤®¬ 襩 бва Ёжл WWW. Ђ¤аҐб ¤®¬ 襩 бва Ёжл Microsoft Internet Explorer ¬®¦® Ё§¬ҐЁвм зҐаҐ§ Ї Ґ«м гЇа ў«ҐЁп Control Panel, § ЇгбвЁў ЇаЁ«®¦ҐЁҐ Internet. ЏаЁ«®¦ҐЁҐ Internet ®в®Ўа ¦ Ґв нЄа Ґ ¤Ё «®Ј®ўго Ї Ґ«м Internet Properties. ‚ Ґ© ¤® ўлЎа вм бва Ёжг Navigation, § ⥬ ўлЎа вм Ё§ бЇЁбЄ Page бва®Єг Start Page ( з «м п Ё«Ё ¤®¬ ипп бва Ёж ) Ё ўўҐбвЁ ҐҐ ¤аҐб ў Ї®«Ґ Address |
|
GoSearch |
ЏҐаҐ©вЁ Є Їа®б¬®ваг Ї®ЁбЄ®ў®© бва Ёжл WWW. Ђ¤аҐб Ї®ЁбЄ®ў®© бва Ёжл Microsoft Internet Explorer ¬®¦® Ё§¬ҐЁвм зҐаҐ§ Ї Ґ«м гЇа ў«ҐЁп Control Panel, § ЇгбвЁў ЇаЁ«®¦ҐЁҐ Internet |
|
Navigate |
ЏҐаҐ©вЁ Є Їа®б¬®ваг бва Ёжл WWW б § ¤ л¬ ¤аҐб®¬. €¬Ґ® нв®в ¬Ґв®¤ ¬л Ўг¤Ґ¬ ЁбЇ®«м§®ў вм ў ЇаЁ«®¦ҐЁЁ Look зв®Ўл ЇҐаҐ©вЁ Є Їа®б¬®ваг ®ЇаҐ¤Ґ«Ґ®© бва Ёжл WWW |
|
Refresh, Refresh2 |
ЋЎ®ўЁвм Ёд®а¬ жЁо ⥪г饩 бва Ёжл WWW |
|
SetHeight |
“бв ў«Ёў Ґв ўҐавЁЄ «мл© а §¬Ґа ®Є (frame window), ᮤҐа¦ 饣® ®аЈ гЇа ў«ҐЁп Web Browser |
|
SetLeft |
“бв ў«Ёў Ґв а ббв®пЁҐ ¬Ґ¦¤г ўгв॥© «Ґў®© бв®а®®© ®аЈ гЇа ў«ҐЁп Web Browser Ё «Ґў®© бв®а®®© ҐЈ® Є®вҐ©Ґа |
|
SetTop |
“бв ў«Ёў Ґв а ббв®пЁҐ ¬Ґ¦¤г ўгв॥© ўҐа奩 бв®а®®© ®аЈ гЇа ў«ҐЁп Web Browser Ё ўҐа奩 бв®а®®© ҐЈ® Є®вҐ©Ґа |
|
SetWidth |
“бв ў«Ёў Ґв Ј®аЁ§®в «мл© а §¬Ґа ®Є (frame window), ᮤҐа¦ 饣® ®аЈ гЇа ў«ҐЁп Web Browser |
|
Stop |
Ћбв ®ўЁвм § Јаг§Єг бва Ёжл WWW |
Љ« бб CWebBrowser, ЇаҐ¤бв ў«пойЁ© ®аЈ гЇа ў«ҐЁп Microsoft Web Browser Control, в Є¦Ґ ᮤҐа¦Ёв жҐ«л© ап¤ ¬Ґв®¤®ў, Є®в®алҐ ўл§лў овбп ў а §«Ёзле бЁвг жЁпе. ‚ б«Ґ¤го饩 в Ў«ЁжҐ ¬л ЇаЁўҐ«Ё бЇЁб®Є нвЁе ¬Ґв®¤®ў Ё ¤ «Ё Ё¬ Єа вЄЁҐ ®ЇЁб Ёп.
|
ЊҐв®¤ |
ЋЇЁб ЁҐ |
|
BeforeNavigate |
‚л§лў Ґвбп Є®Ј¤ ўЁЈ в®а ЇҐаҐе®¤Ёв Є Їа®б¬®ваг ¤агЈ®Ј® URL |
|
CommandStateChange |
‚лЇ®«ҐЁҐ ®ЇаҐ¤Ґ«Ґле Є®¬ ¤ а §аҐиҐ® Ё«Ё § ЇаҐйҐ® |
|
DownloadBegin |
Ќ з « бм § Јаг§Є аҐбгаб |
|
DownloadComplete |
‡ Јаг§Є аҐбгаб § ўҐаи Ґвбп, ®бв ў«Ёў Ґвбп Ё«Ё ЇаҐалў Ґвбп |
|
FrameBeforeNavigate |
Ќ ўЁЈ в®а ЇаЁбвгЇ Ґв Є § Јаг§ЄҐ ®ў®Ј® аҐбгаб б ¤агЈЁ¬ ¤аҐб®¬ URL. Џа®Ёб室Ёв, Ґб«Ё ¤ ®© бва ЁжҐ WWW ЇаЁбгвбвўгов д३¬л |
|
FrameNavigateComplete |
‚л§лў Ґвбп Ї®б«Ґ в®Ј®, Є Є ўЁЈ в®а § Јаг§Ёв ®ўл© аҐбгаб. |
|
FrameNewWindow |
‘®§¤ ® ®ў®Ґ ®Є®. Џа®Ёб室Ёв, Ґб«Ё ¤ ®© бва ЁжҐ WWW ЇаЁбгвбвўгов д३¬л |
|
NavigateComplete |
Ќ ўЁЈ в®а гбЇҐи® § Јаг§Ё« ®ўл© аҐбгаб |
|
NewWindow |
Ќ®ў®Ґ ®Є® ¤®«¦® Ўлвм б®§¤ ® ¤«п ®в®Ўа ¦ҐЁп аҐбгабб |
|
ProgressChange |
€§¬ҐЁ«®бм б®бв®пЁҐ Їа®жҐбб § Јаг§ЄЁ |
|
Quit |
ЏаЁ«®¦ҐЁҐ Internet Explorer Ј®в®ў® § ўҐаиЁвмбп |
|
StatusTextChange |
€§¬ҐпҐвбп ⥪бв ў Ї Ґ«Ё б®бв®пЁп |
|
TitleChange |
‡ Ј®«®ў®Є ¤®Єг¬Ґв ў ®ЄҐ ўЁЈ в®а Ї®«гзҐ Ё«Ё Ё§¬ҐҐ |
|
WindowActivate |
ЋЄ® ўЁЈ в®а Ї®«гзЁ«® гЇа ў«ҐЁҐ |
|
WindowMove |
ЋЄ® ўЁЈ в®а ЇҐаҐ¬ҐбвЁ«®бм |
|
WindowResize |
€§¬ҐҐ а §¬Ґа ®Є ўЁЈ в®а |
Классы WinInet
Библиотека MFC значительно упрощают использование программного интерфейса WinInet. Начиная с версии 4.2 в MFC добавлен целый ряд новых классов созданных на основе программного интерфейса WinInet.Рисунок 1.1 представляет вашему вниманию схему классов WinInet.
CObject -> CInternetSession
|
-> CInternetConnection -> CHttpConnection
| |
| -> CFtpConnection
| |
| -> CGopherConnection
|
-> CFile -> CStdioFile -> CInternetFile -> CHttpFile
| |
| -> CGopherFile
|
-> CFileFind -> CFtpFileFind
| |
| -> CGopherFileFind
|
-> CGopherLocator
|
-> CException -> CInternetException
Рис. 1.1. Схема классов WinInet
Более полное описание классам MFC, предназначенным для работы с WinInet, мы дадим несколько позже, а сейчас только представим их список с краткими пояснениями:
| Класс | Описание | ||
| CInternetSession | Класс подготавливает сеанс связи с Internet. Это самый общий класс, который необходимо использовать для работы с другими классами WinInet | ||
| CInternetConnection | Управляет соединением с серверами в Internet вне зависимости от их типа (FTP, WWW, Gopher) | ||
| CFtpConnection | Управляет соединением с серверами FTP по протоколу FTP. Позволяет выполнять манипуляции с файлами и каталогами сервера | ||
| CHttpConnection | Управляет соединением с серверами WWW по протоколу HTTP | ||
| CGopherConnection | Используется для соединения с серверами Gopher | ||
| CInternetFile | Используется для получения файлов с серверов WWW и Gopher | ||
| CHttpFile | Класс позволяет передать запрос серверу WWW и получить от него ответ (данные) | ||
| CGopherFile | Предназначен для работы с серверами Gopher | ||
| CFileFind | Используется для поиска файлов | ||
| CFtpFileFind | Выполняет поиск файлов на сервере FTP | ||
| CGopherFileFind | Выполняет поиск файлов на сервере Gopher | ||
| CGopherLocator | Создает объект, связанный с файлом Gopher | ||
| CInternetException | Представляет исключения, связанные с ошибками при работе WinInet |
Практически все классы библиотеки MFC, управляющие интерфейсом WinInet, а также несколько глобальных функций WinInet определены во включаемом файле afxinet.h. Исключение составляет только класс CFileFind, который определен в другом включаемом файле библиотеки MFC - afx.h. Данные включаемые файлы должны быть указаны в исходных текстах приложений, работающих с интерфейсом WinInet:
#include
#include
Кнопки “Вперед” и “Назад”
В нашем варианте навигатора отсутствуют кнопки, позволяющие вернуться к просмотру страниц WWW, которые вы уже посетили во время текущего сеанса связи. Это крайне неудобно, так как затрудняет обход дерева гипертекстовых ссылок серверов WWW.Добавить возможность возврата к предыдущим страницам WWW очень просто. Орган управления Microsoft Web Browser Control сам запоминает путь ваших хождений по паутине Internet. В состав класса CWebBrowser входят методы GoBack и GoForward, которые позволяют просматривать уже посещенные вами страницы.
Методы GoBack и GoForward имеют простой прототип - у них отсутствуют параметры и они не возвращают никакого значения. Интересно, что в документации на Microsoft Web Browser Control, которую мы получили через сеть Internet, указывается, что эти методы возвращают значение типа HRESULT, определяющее результат выполнения метода:
void CWebBrowser::GoBack()
void CWebBrowser::GoForward()
С помощью редактора ресурсов добавьте к диалоговой панели IDD_LOOK_DIALOG приложения Look, две кнопки. Первую назовите Back (назад) и присвойте ей идентификатор IDC_BACK, а вторую - Forward (вперед) и воспользуйтесь идентификатором IDC_FORWARD.
После того как кнопки заняли свое место в диалоговой панели, запустите MFC ClassWizard и добавьте к классу CLookDlg два метода для обработки командных сообщений от кнопок Back и Forward. Согласитесь с предложением MFC ClassWizard и оставьте для них названия OnBack и OnForward.
Затем непосредственно из MFC ClassWizard перейдите к просмотру и редактированию методов OnBack и OnForward, выбрав одно из названий этих методов из списка и нажав кнопку Edit code. Добавьте к OnBack и OnForward вызовы, соответственно, методов GoBack и GoForward класса CWebBrowser.
Постройте проект и запустите приложение. Посетите несколько страниц WWW и попробуйте вернуться к уже просмотренным страницам, воспользовавшись кнопками Back и Forward. Вы обнаружите, что кнопки работают правильно, но когда вы выходите за границу уже просмотренного материала, на экране появляется ужасная диалоговая панель, показанная на рисунке 4.13.

Рис. 4.13. Предупреждающая диалоговая панель
Класс CWebBrowser имеет средства для управления кнопками просмотра Back и Forward. Для этого предназначен виртуальный метод CommandStateChange, который вызывается самим органом управления. Чтобы переназначить метод CommandStateChange надо воспользоваться средствами MFC ClassWizard (рис. 3.4):
void CBrowserDlg::OnCommandStateChangeExplorer(long Command, BOOL Enable)
{
}
В качестве значения параметра Command, методу CommandStateChange передается отдна из следующих констант - CSC_UPDATECOMMANDS, CSC_NAVIGATEFORWARD или CSC_NAVIGATEBACK. Эти константы определяются следующим образом:
typedef enum CommandStateChangeConstants {
CSC_UPDATECOMMANDS = 0xFFFFFFFF,
CSC_NAVIGATEFORWARD = 0x00000001,
CSC_NAVIGATEBACK = 0x00000002
} CommandStateChangeConstants;
Второй параметр Enable определяет, новое состояние кнопки - заблокирована или доступна. Когда требуется изменить состояние кнопки Back, вызывается метод OnCommandStateChangeExplorer. В качестве первого параметра ему передается значение CSC_NAVIGATEBACK. Когда надо изменить состояние кнопки Forward, в первом параметре передается значение CSC_NAVIGATEFORWARD.
Чтобы управлять состоянием кнопок IDC_BACK и IDC_FORWARD нужно сначала привязать к ним две переменные. Для этого следует воспользоваться средствами MFC ClassWizard.
Откройте в редакторе ресурсов диалоговую панель приложения и выберите в ней кнопку Forward или Back. Запустите MFC ClassWizard. Выберите в панели MFC ClassWizard страницу Member Variables. В списке идентификаторов органов управления диалоговой панели Control IDs укажите идентификатор IDC_BACK, принадлежащий кнопке Back и нажмите кнопку Add Variables. На экране появится диалоговая панель Add Member Variables (рис. 4.14). Введите в поле Member Variables name имя переменной, которая будет связана с кнопкой. Мы предлагаем использовать имя m_Back.

Рис. 4.14. Диалоговая панель Add Member Variables
Обратите внимание, что из списка категории органа управления Category должна быть выбрана строка Control, а из списка типа переменной Variables type - строка CButton. Таким образом мы привяжем к кнопке переменную m_Back класса CButton.
Выбирая из списка Category строку Control мы указываем, что нам надо привязать к органу управления объект соответствующего класса, через который мы затем сможем иметь над ним полный контроль.
¨ Как мы говорили в предыдущих томах серии БСП, посвященных библиотеке MFC, к одному органу управления с помощью MFC ClassWizard можно привязать несколько переменных. Однако это верно только если указывается категория Value. Если вы используете категорию Control, то к одному органу управления можно привязать только одну переменную. Попытка привязать еще одну переменную вызывает сообщение об ошибке (рис. 4.15)

Рис. 4.15. Предупреждение
Повторите описанную процедуру и привяжите к кнопке IDC_FORWARD переменную m_Forward класса CButton.
Когда вы привяжите к кнопкам переменные, MFC ClassWizard внесет изменения в исходные тексты приложения. Во-первых в класс CLookDlg, управляющий диалоговой панелью будет включены два новых элемента данных, представляющих кнопки:
class CLookDlg : public CDialog
{
public:
CLookDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CLookDlg)
enum { IDD = IDD_LOOK_DIALOG };
CButton m_Forward; // Кнопка Forward
CButton m_Back; // Кнопка Back
CWebBrowser m_explorer;
CString m_address;
//}}AFX_DATA
...
Во-вторых, будут добавлены новые записи в таблице обмена данными, расположенной в методе DoDataExchange класса CLookDlg. Новые записи являются вызовами методов DDX_Control, которые выполняют обмен данными между органом управления и привязанной к нему переменной:
void CLookDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLookDlg)
DDX_Control(pDX, IDC_FORWARD, m_Forward); // Кнопка Forward
DDX_Control(pDX, IDC_BACK, m_Back); // Кнопка Back
DDX_Control(pDX, IDC_EXPLORER, m_explorer);
DDX_CBString(pDX, IDC_COMBO_ADDRESS, m_address);
//}}AFX_DATA_MAP
}
Конструктор класса CFtpFileFind
Конструктор класса CFtpFileFind имеет только один обязательный параметр pConnection. В нем вы должны указать объект класса CFtpConnection, представляющий соединение с сервером FTP, в рамках которого вы будете осуществлять поиск файлов или каталогов:CFtpFileFind(
CFtpConnection* pConnection,
DWORD dwContext = 1
);
В качестве второго параметра вы можете указать идентификатор контекста, который будет соответствовать данному объекту класса CFtpFileFind.
Конструктор класса CFtpViewDlg
Конструктор класса CFtpViewDlg вызывается при создании объекта соответствующего класса, которое выполняется методом InitInstance главного класса приложения CFtpViewApp:CFtpViewDlg dlg;
Конструктор класса CFtpViewDlg вызывает конструктор своего базового класса CDialog. При этом ему передается идентификатор диалоговой панели IDD и идентификатор главного окна приложения pParent. Диалоговая панель нашего приложения имеет идентификатор IDD_FTPVIEW_DIALOG, но в определении класса CFtpViewDlg указано, что IDD соответствует IDD_FTPVIEW_DIALOG:
enum { IDD = IDD_FTPVIEW_DIALOG };
В теле конструктора расположен блок AFX_DATA_INIT. В нем ClassWizard поместил код инициализации элемента данных класса m_FtpAddress. Конструктор также инициализирует m_hIcon, записывая в него идентификатор пиктограммы IDR_MAINFRAME:
//============================================================
// Конструктор класса CFtpViewDlg
//============================================================
CFtpViewDlg::CFtpViewDlg(CWnd* pParent /*=NULL*/)
: CDialog(CFtpViewDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CFtpViewDlg)
m_FtpAddress = _T("");
//}}AFX_DATA_INIT
// При использовании метода LoadIcon нет необходимости
// вызывать DestroyIcon
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
Конструктор класса CInternetSession
Конструктор класса CInternetSession имеет следующий прототип:CInternetSession(
LPCTSTR pstrAgent = NULL,
DWORD dwContext = 1,
DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS,
LPCTSTR pstrProxyName = NULL,
LPCTSTR pstrProxyBypass = NULL,
DWORD dwFlags = 0
);
Посмотрите, как определен конструктор класса CInternetSession в исходных текстах библиотеки MFC. Вы найдете соответствующий программный код в файле Inet.cpp.
Конструктор класса CInternetSession вызывает единственную функцию программного интерфейса WinInet - InternetOpen, которая выполняет инициализацию WinInet.
Параметры конструктора класса CInternetSession передаются непосредственно функции InternetOpen:
m_dwContext = dwContext;
if (pstrAgent == NULL)
pstrAgent = AfxGetAppName();
m_hSession = InternetOpen(pstrAgent, dwAccessType,
pstrProxyName, pstrProxyBypass, dwFlags);
Вернемся теперь к обсуждению параметров конструктора. Все параметры конструктора класса CInternetSession являются необязательными и при создании объекта данного класса вы их можете не указывать.
Через параметр pstrAgent можно явным образом указать название приложения, или программного модуля, работающего с WinInet. Это название используется протоколом HTTP, и передается серверу в качестве имени пользовательского агента.
Если вызвать конструктор класса CInternetSession без параметров или указать в качестве параметра pstrAgent значение NULL, то конструктор самостоятельно определит имя приложения. Для этого конструктор использует глобальную функцию AfxGetAppName (см. фрагмент из файла Inet.cpp представленный выше).
Параметр dwContext определяет идентификатор контекста. Созданный объект будет ассоциироваться с данным идентификатором контекста.
Параметр dwAccessType определяет тип доступа к сети. Можно использовать одно из значений, перечисленных в следующей таблице:
| Константа | Тип доступа | ||
| INTERNET_OPEN_TYPE_PRECONFIG | Используется тип доступа, указанный в регистрационной базе Windows | ||
| INTERNET_OPEN_TYPE_DIRECT | Прямой доступ к Internet | ||
| INTERNET_OPEN_TYPE_PROXY | Доступ через proxy сервер |
![]() |
Откройте Control Panel и выберите приложение Internet, которое предназначено для настройки различных параметров доступа к сети Internet |

Рис. 1.2. Диалоговая панель Internet Properties
Если включен переключатель Connect to the Internet as needed в группе Dialing, значит ваш компьютер подключается к сети через модем с использованием Dial-Up Networking. Если включен переключатель Connect through a proxy server, значит вы подключены к сети через специальный proxy сервер. Настройки этого сервера вы можете просмотреть, нажав кнопку Setting. В случае, если оба эти переключателя отключены, значит вы подключены к сети напрямую.
Константы PRE_CONFIG_INTERNET_ACCESS и INTERNET_OPEN_TYPE_PRECONFIG определены в файле wininet.h. Ниже мы привели соответствующий фрагмент этого файла:
//
// access types for InternetOpen()
//
#define INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration
#define INTERNET_OPEN_TYPE_DIRECT 1 // direct to net
#define INTERNET_OPEN_TYPE_PROXY 3 // via named proxy
#define PRE_CONFIG_INTERNET_ACCESS INTERNET_OPEN_TYPE_PRECONFIG
#define LOCAL_INTERNET_ACCESS INTERNET_OPEN_TYPE_DIRECT
#define GATEWAY_INTERNET_ACCESS 2 // Internet via gateway
#define CERN_PROXY_INTERNET_ACCESS INTERNET_OPEN_TYPE_PROXY
Как видите, значения констант PRE_CONFIG_INTERNET_ACCESS и INTERNET_OPEN_TYPE_PRECONFIG полностью совпадают.
Если тип доступа, определен параметром dwAccessType как INTERNET_OPEN_TYPE_PROXY, то через параметр pstrProxyName следует указать имя proxy сервера, а через параметр pstrProxyBypass - указатель на строку со списком адресов дополнительных серверов. Вы можете указать в качестве параметра pstrProxyBypass значение NULL. При этом список серверов будет взят из регистрационной базы данных Windows.
Если используются другие типы доступа, то в качестве параметра dwAccessType надо указать значение NULL. Параметр pstrProxyBypass в этом случае значения не имеет.
Последний параметр конструктора dwFlags определяет различные характеристики, перечисленные в следующей таблице:
|
Константа |
Описание |
|
INTERNET_FLAG_DONT_CACHE |
Данные, получаемые WinInet из сети кэшируются в специальном каталоге на жестком диске локального компьютера. В случае если пользователь часто запрашивает у WinInet одни и те же файлы, то для ускорения они могут браться не из сети, а из кэша. Константа INTERNET_FLAG_DONT_CACHE запрещает кэшировть принимаемые данные |
|
INTERNET_FLAG_ASYNC |
Устанавливает асинхронный режим работы WinInet. Последующие операции с объектом могут завершится с ошибкой ERROR_IO_PENDING. Когда операция закончится, функция обратного вызова, будет вызвана с кодом INTERNET_STATUS_REQUEST_COMPLETE |
|
INTERNET_FLAG_OFFLINE |
Операции по загрузке информации выполняются только из кэша. В том случае, если запрашиваемая информация в кэше отсутствует, возвращается ошибка |
Конструктор класса CLookDlg
Конструктор класса CLookDlg вызывается при создании объекта данного класса методом InitInstance главного класса приложения. Он вызывает конструктор базового класса CDialog. При этом ему указывается идентификатор диалоговой панели IDD, который определен в классе CLookDlg следующим образом:enum { IDD = IDD_LOOK_DIALOG };
Конструктор базового класса загружает шаблон диалоговой панели IDD_LOOK_DIALOG и подготавливает все к отображению ее на экране.
Затем выполняется инициализация строк m_address, m_StatusText и m_TitleBar и загружается пиктограмма приложения:
// Инициализируем строки
//{{AFX_DATA_INIT
m_address = _T("");
m_StatusText = _T("");
m_TitleBar = _T("");
//}}AFX_DATA_INIT
// Загружаем пиктограмму приложения
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
Литература
1.Фролов А. В., Фролов Г. В. Microsoft Visual C++ и MFC: В 2 ч. М.: Диалог-МИФИ, 1996. (Б-ка системного программиста; Т.24, 28.).
2. Фролов А. В., Фролов Г. В. Операционная система Microsoft Windows 3.1 для программиста: В 3 ч. М.: Диалог-МИФИ, 1994. (Б-ка системного программиста; Т.11-13.).
3. Фролов А. В., Фролов Г. В. Графический интерфейс GDI в Microsoft Windows. М.: Диалог-МИФИ, 1994. (Б-ка системного программиста; Т.14.).
4. Фролов А. В., Фролов Г. В. Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы. М.: Диалог-МИФИ, 1994. (Б-ка системного программиста; Т.17.).
5. Фролов А. В., Фролов Г. В. Операционная система Microsoft Windows 95 для программиста. М.: Диалог-МИФИ, 1994. (Б-ка системного программиста; Т.22.).
6. Фролов А. В., Фролов Г. В. Программирование для Windows NT: В 2 ч. М.: Диалог-МИФИ, 1996. (Б-ка системного программиста; Т.26-27.).
7. Viktor Toth. Visual C++™ 4 Unleashed. Sams Publishing, 1996.
Локальный вариант WebHelp
В приложении WebHelp файлы справочной системы приложения находятся непосредственно в Internet. Измените приложение так, чтобы файлы справочной системы можно было расположить на локальном диске компьютера. Для этого достаточно записать соответствующий htm файл на жесткий диск локального компьютера и соответствующим образом изменить третий параметр функции ShellExecute, так чтобы он указывал на локальный файл.Запустите измененное приложение WebHelp и выберите из меню Help строку Web Help. Сразу запустится навигатор Internet Explorer и в нем появится описание приложения. При этом совершенно необязательно, чтобы компьютер был соединен с сетью Internet. Более того, компьютер вообще может не иметь ни модема, ни сетевой карты - справочная система все равно будет работать, так как HTML файлы со справочной информацией расположены на локальном диске компьютера.
Метод AddRequestHeaders
Метод AddRequestHeaders добавляет один или несколько заголовков к запросу HTTP, который затем будет передаваться на сервер. Данный метод только подготавливает запрос HTTP, для выполнения фактической передачи запроса серверу необходимо воспользоваться методом SendRequest класса CHttpFile.В классе CHttpFile определены два метода AddRequestHeaders, которые имеют различный набор параметров:
BOOL AddRequestHeaders(
LPCTSTR pstrHeaders,
DWORD dwFlags = HTTP_ADDREQ_FLAG_ADD_IF_NEW,
int dwHeadersLen = -1
);
BOOL AddRequestHeaders(
CString& str,
DWORD dwFlags = HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
Строка с заголовками, которые надо добавить к запросу, указывается методу AddRequestHeaders через параметр pstrHeaders, как указатель типа LPCTSTR или через строку str класса CString. В первом случае вы должны использовать строку, закрытую нулем, или специально указать длину строки при помощи параметра dwHeadersLen. Если вы добавляете к запросу сразу несколько заголовков, то они должны быть разделены парой символов - CR и LF.
Параметр dwFlags позволяет изменить способ добавления заголовков к запросу. Для этого вы должны указать в качестве этого параметра один из флагов, представленных в следующей таблице:
| Флаг | Описание | ||
| HTTP_ADDREQ_FLAG_COALESCE | Объединить заголовки, которое имеют одинаковые имена | ||
| HTTP_ADDREQ_FLAG_REPLACE | Замещает заголовок с указанным именем. Если методу AddRequestHeaders передается только название заголовка, без значения, то соответствующий заголовок удаляется из запроса. Если вы используете этот режим добавления, то вы можете указать методу AddRequestHeaders только один заголовок. Совместно с этим флагом может использоваться флаг HTTP_ADDREQ_FLAG_ADD | ||
| HTTP_ADDREQ_FLAG_ADD_IF_NEW | Добавляет новый заголовок только в том случае, если заголовок с таким именем отсутствует. В противном случае метод AddRequestHeaders завершается с ошибкой | ||
| HTTP_ADDREQ_FLAG_ADD | Добавляет заголовок только в том случае, если заголовок с таким именем отсутствует. Может использоваться совместно с флагом HTTP_ADDREQ_FLAG_REPLACE |
В случае успешного завершения метод AddRequestHeaders возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод Close
Одно приложение может создать несколько объектов класса CInternetSession. Объект CInternetSession должен существовать на протяжении всего сеанса связи с Internet. Когда вам понадобится завершить работу с Internet, вызовите для объекта CInternetSession виртуальный метод Close, определенный в классе CInternetSession:virtual void Close();
Следует отметить, что деструктор класса CInternetSession сам вызывает метод Close.
По окончании работы с файлом на сервере, его следует закрыть. Для этого вы можете воспользоваться методом Close:
virtual void Close();
throw( CInternetException );
В случае ошибки, метод Close может вызвать исключение CInternetException. Поэтому вы должны учесть такую возможность и поместить вызов этого метода внутри блока try.
Виртуальный метод Close закрывает объект CHttpFile и освобождает все используемые им ресурсы. Вы можете использовать данный метод после передачи запроса серверу с методом SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.
Прототип метода Close представлен ниже:
virtual void Close( );
Метод Close не имеет параметров и не возвращает значения.
Когда вы завершите поиск файлов, следует вызвать метод Close. Он завершает поиск и освобождает все задействованные ресурсы. После того как вы вызвали метод Close, вы можете повторить поиск, используя тот же самый объект CFileFind:
void Close( );
Метод DirectoryView
Метод DirectoryView класса CFtpViewDlg выполняет львиную долю работы всего нашего приложения. Именно этот метод после соединения с сервером FTP определяет названия и основные характеристики объектов в заданном каталоге сервера, а затем отображает их в списке IDC_FTP_LIST на главной диалоговой панели приложения:BOOL CFtpViewDlg::DirectoryView()
{
// Переменная, сигнализирующая о получении последнего
// элемента каталога
BOOL fResult;
// Временная переменная, определяющая тип объекта -
// файл или каталог
BOOL fDirectory;
Метод DirectoryView заполняет список IDC_FTP_LIST именами каталогов и файлов сервера FTP. Чтобы удалить из списка элементы, занесенные в него ранее, вызываем метод DeleteAllItems для объекта m_FtpList, управляющего этим списком (см. метод DoDataExchange):
// Удалить все элементы из списка IDC_FTP_LIST
m_FtpList.DeleteAllItems();
Для поиска файлов и каталогов на сервере FTP предназначен класс CFtpFileFind, входящий в состав библиотеки классов MFC. Метод DirectoryView создает объект этого класса. В качестве параметра конструктору класса CFtpFileFind передается указатель m_FtpConnection на объект, представляющий соединение с сервером FTP:
CFtpFileFind m_FtpFileFind(m_FtpConnection);
Далее мы вызываем для только что созданного объекта m_FtpFileFind метод FindFile. Он выполняет поиск каталогов и файлов в каталоге, путь которого указан в качестве параметра:
// Получаем имена всех объектов текущего каталога
if(fResult =
m_FtpFileFind.FindFile(_T(sCurentDirectory + "/*")))
{
. . .
}
Если каталог не содержит других объектов - каталогов и файлов, или в случае возникновения ошибки, метод FindFile возвращает ненулевое значение. Тогда на экран выводится сообщение File's not found or error и работа метода DirectoryView завершается.
В случае, если метод FindFile завершается успешно, это означает, что указанный каталог содержит другие каталоги или файлы. Тогда мы начинаем в цикле вызывать метод FindNextFile. Он получает имя очередного файла или каталога и мы отображаем его вместе с некоторой дополнительной информацией в списке объектов каталога. Когда метод FindNextFile вернет значение FALSE, это значит, что мы считали последний объект каталога. В этом случае мы добавляем этот последний объект к списку, завершаем цикл и выходим из метода DirectoryView:
for(int n = 0;fResult; n++)
{
// Получаем очередной объект из каталога
fResult = m_FtpFileFind.FindNextFile();
. . .
}
После обращения к методу FindNextFile можно вызывать другие методы класса CFtpFileFind. Они позволяют определить различные характеристики обнаруженных объектов. Например, вы можете узнать являются ли они каталогами или файлами, получить их имя, размер, дату создания и т. д.
Так, сначала мы определяем, является ли очередной объект, полученный методом FindNextFile файлом или каталогом и записываем полученный результат в fDirectory. Если мы получили каталог, то метод IsDirectory возвращает значение 1, а если файл, то - 0. Мы будем использовать значение fDirectory, сравнивая его с константой DIRECTORY, определенной как 0 и FILE, определенной как 1 в файле FtpViewDlg.h.
Далее с помощью метода GetFileName определяется имя объекта (файла или каталога) и записывается во временную строку fileName класса CString:
CString fileName;
// Определяем имя объекта
fileName = m_FtpFileFind.GetFileName();
Теперь, когда мы узнали тип объекта и его имя можно добавить элемент к первой колонке в списке. Для этого необходимо заполнить поля структуры lv_item и передать ее методу InsertItem объекта, управляющего списком:
// Структура для добавления нового элемента к списку
LV_ITEM lv_item;
. . .
// Заполняем структуру lv_item, сведениями об
// очередном объекте каталога сервера FTP. Указываем,
// что в список добавляется текст и изображение
lv_item.mask = LVIF_TEXT | LVIF_IMAGE;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем первую колонку
lv_item.iSubItem = 0;
// Выбираем изображение для нового элемента списка в
// зависимости от типа объекта
lv_item.iImage = (fDirectory) ? DIRECTORY : FILE;
// Указываем имя каталога или файла
lv_item.pszText = fileName.GetBuffer(MIN_LEN_BUF);
// Добавляем новый элемент к списку IDC_FTP_LIST
m_FtpList.InsertItem(&lv_item);
После заполнения первой колонки списка необходимо заполнить вторую, записав в нее длину файла или, если данный элемент списка описывает каталог, то строку Dir. Точно также как в случае с именем объекта, сначала заполняется структура lv_item, а затем она передается методу InsertItem.
Обратите внимание, что в отличие от первой колонки, во второй колонки не используется пиктограмма, поэтому в поле mask структуры lv_item заносится только константа LVIF_TEXT. Другое отличие состоит в том, что полю iSubItem структуры lv_item присваивается значение 1, указывающее, что мы будем добавлять элемент во вторую колонку (нумерация iSubItem начинается с нуля):
//============= Определяем длину файла =============
// Длинна файла
DWORD dwLength = 0;
// Временные строка для формирования текстового
// представления длинны файла
CString sLength;
// Заполняем колонку Length для новой записи и
// записываем в нее длину файла или строку Dir, если
// новый объект - каталог.
// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем вторую колонку
lv_item.iSubItem = 1;
// Если очередной объект является каталогом, то
// вместо в колонке Length записываем строку Dir
if(fDirectory)
{
lv_item.pszText = "Dir";
}
// Если очередной объект является файлом, то
// записываем его длину в колонку Length
else
{
// Определяем длину файла
dwLength = m_FtpFileFind.GetLength();
// Формируем текстовое представление длины файла
sLength.Format("%d", dwLength);
lv_item.pszText = sLength.GetBuffer(MIN_LEN_BUF);
}
// Добавляем запись во второй колонке списка (колонка
// Length)
m_FtpList.SetItem(&lv_item);
Теперь остается заполнить только третью и четвертую колонки списка, в которых надо записать дату и время создания или изменения файла (каталога). Для определения этих данных вызывается метод GetLastWriteTime класса CFtpFileFind, который записывает полученную информацию в объект mTime класса CTime:
// Дата и время создания каталога или файла
CTime mTime;
// Временные строки для формирования текстового
// представления даты и времени
CString sDate;
CString sTime;
// Определяем время изменения файла или каталога
if(!m_FtpFileFind.GetLastWriteTime(mTime))
break;
Затем мы подготавливаем структуру lv_item, заполняя ее поля соответствующими значениями:
// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT;
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем третью колонку
lv_item.iSubItem = 2;
// Выделяем из объекта mTime день, месяц и год
sDate = mTime.Format("%d.%m.%y");
// Записываем сформированную дату в структуру lv_item
lv_item.pszText = sDate.GetBuffer(MIN_LEN_BUF);
Чтобы добавить запись в колонке Date обращаемся к методу SetItem и передаем ему указатель на структуру lv_item:
// Добавляем запись во второй колонке списка (колонка Date)
m_FtpList.SetItem(&lv_item);
Затем мы немного модифицируем только что заполненную структуру lv_item, изменяя в ней только поля iSubItem и pszText:
// Заполняем четвертую колонку, записываем в нее
// время последнего изменения файла (каталога)
lv_item.iSubItem = 3;
// Выделяем из объекта mTime часы, минуты и секунды
sTime = mTime.Format("%H:%M:%S");
// Записываем сформированную строку, содержащую время
lv_item.pszText = sTime.GetBuffer(MIN_LEN_BUF);
Опять вызываем метод SetItem, передавая ему модифицированную структуру lv_item. Этот вызов добавит информацию о времени последнего изменения файла в колонке Time:
// Добавляем запись во второй колонке списка (колонка Time)
m_FtpList.SetItem(&lv_item);
Когда при очередном вызове метод FindNextFile возвращает нулевое значение, значит найден последний объект из каталога. В список добавляется последняя строка и на этом работа цикла заполнения списка завершается. Заканчиваем поиск объектов в данном каталоге вызывая метод Close и возвращаем управление с помощью оператора return:
// Заканчиваем поиск объектов в каталоге, закрываем
// объект m_FtpFileFind
m_FtpFileFind.Close();
Метод DoDataExchange
С помощью MFC ClassWizard мы привязали к органам управления диалоговой панели несколько переменных. Всю работу по связыванию этих переменных и органов управления выполняет метод DoDataExchange. В блоке AFX_DATA_MAP размещены вызовы соответствующих методов DDX://============================================================
// Метод DoDataExchange класса CFtpViewDlg
//============================================================
void CFtpViewDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CFtpViewDlg)
DDX_Control(pDX, IDC_STATUS, m_Status);
DDX_Control(pDX, IDOK, m_Ok);
DDX_Control(pDX, IDC_ON_TOP, m_OnTop);
DDX_Control(pDX, IDC_CONNECT, m_Connect);
DDX_Control(pDX, IDC_FTP_LIST, m_FtpList);
DDX_Text(pDX, IDC_FTP_ADDRESS, m_FtpAddress);
//}}AFX_DATA_MAP
}
Обратите внимание, что для большей части органов управления диалоговой панели используются обращения к методу DDX_Control. Он осуществляет привязку к органу диалоговой панели управляющего объекта соответствующего класса. Вызывая методы этого объекта можно выполнять над органом управления различные действия.
Только последний метод в блоке AFX_DATA_MAP отличается от остальных. Это метод DDX_Text, который используется для обмена данными между полем редактирования IDC_FTP_ADDRESS и строкой m_FtpAddress. Обмен выполняется при обращении к методу UpdateData.
Метод DoDataExchange класса CFtpViewDlg создается и модифицируется средствами MFC AppWizard и MFC ClassWizard. В большинстве случаев от вас не потребуется изменять этот метод вручную.
При разработке приложения мы привязали к некоторым органам управления диалоговой панели приложения соответствующие элементы данных и ввели их в состав класса CLookDlg. Чтобы обеспечить связь между этими органами управления и элементами данных класса, MFC ClassWizard добавил в блоке AFX_DATA_MAP метода DoDataExchange ряд методов DDX_:
DDX_Control(pDX, IDC_PROGRESS, m_Progress);
DDX_Control(pDX, IDC_EXPLORER, m_explorer);
DDX_Text(pDX, IDC_STATUS_TEXT, m_StatusText);
DDX_Text(pDX, IDC_TITLE_BAR, m_TitleBar);
DDX_CBString(pDX, IDC_COMBO_ADDRESS, m_address);
Линейный индикатор IDC_PROGRESS и навигатор IDC_EXPLORER связываются с элементами данных m_Progress и m_explorer при помощи методов DDX_Control. Таким образом, мы можем полностью управлять линейным индикатором и навигатором вызывая методы соответствующих классов.
Поля редактирования IDC_STATUS_TEXT и IDC_TITLE_BAR связываются со строками m_StatusText и m_TitleBar методами DDX_Text. Чтобы выполнить обмен данными между этими строками и полями редактирования мы будем обращаться к методу UpdateData.
И, наконец, последний метод в блоке DDX_CBString связывает список IDC_COMBO_ADDRESS со строкой m_address. Для того, чтобы узнать текущее состояние списка мы также будем обращаться к методу UpdateData.
Метод EnableStatusCallback
Чтобы разрешить программному интерфейсу WinInet вызывать метод OnStatusCallback, необходимо воспользоваться методом EnableStatusCallback, прототип которого мы представили ниже:BOOL
EnableStatusCallback( BOOL bEnable = TRUE );
throw ( CInternetException );
Метод EnableStatusCallback имеет только один параметр, который разрешает или запрещает использование метода OnStatusCallback. По умолчанию параметр bEnable содержит значение TRUE, которое разрешает его использование.
Метод EnableStatusCallback возвращает ненулевое значение в случае успешного завершения или ноль в случае ошибки. Данный метод может вызывать исключение CInternetException. Вы можете определить причину исключения, воспользовавшись методами класса CInternetException.
Метод FindFile
Чтобы приступить к поиску файла или каталога с заданным имененм надо воспользоваться методом FindFile. Он также позволяет приступить к поиску файлов и каталогов по шаблону имени. Прототип метода FindFile представлен ниже:virtual BOOL FindFile(
LPCTSTR pstrName = NULL,
DWORD dwFlags = INTERNET_FLAG_RELOAD
);
Имя файла или каталога, который вы желаете найти или соответствующий этому имени шаблон, вы должны записать в строку и передать указатель на нее методу FindFile через параметр pstrName. Чтобы выполнить поиск всех объектов - и файлов и каталогов, расположенных в текущем каталоге сервера FTP, вы можете указать в качестве шаблона для поиска символ звездочки * или просто использовать в качестве параметра pstrName значение NULL. Такой же эффект получается если вы просто опустите параметры метода и для них будут использованы значения, принятые по умолчанию.
Параметр dwFlags позволяет управлять процессом поиска. Вы, например, можете указать методу FindFile, что поиск следует осуществлять не используя данные которые записаны в кэш. Вот список флагов, которые можно использовать:
¨ INTERNET_FLAG_RELOAD
¨ INTERNET_FLAG_DONT_CACHE
¨ INTERNET_OPEN_FLAG_RAW_DATA
¨ INTERNET_FLAG_SECURE
¨ INTERNET_OPEN_FLAG_USE_EXISTING
Их описание мы приводили, когда рассказывали о методе OpenRequest класса CHttpConnection.
В случае успешного завершения метод AddRequestHeaders возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод FindNextFile
После успешно завершившегося вызова метода FindFile вы должны вызвать виртуальный метод FindNextFile один или несколько раз для поиска всех файлов, соответствующих вашим требованиям. Только после того как вы хотя бы один раз вызвали метод FindNextFile вы можете воспользоваться другими методами класса CFindFile, чтобы определить характеристики обнаруженных файлов:virtual BOOL FindNextFile( );
Метод FindNextFile возвращает ненулевое значение, если вы можете продолжить поиск и снова вызвать метод FindNextFile. Если метод FindNextFile вернул нулевое значение, значит был обнаружен последний файл соответствующий указанному вами шаблону поиска. В этом случае вы можете определить его характеристики, воспользовавшись методами класса FindFile и должны закончить поиск.
Метод FtpFileDownload класса CFtpViewDlg
Метод FtpFileDownload класса CFtpViewDlg имеет единственный параметр sFileName, через который ему передается строка, содержащая имя файла на сервере FTP. Этот файл надо загрузить на локальный компьютер:BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
}
Первая часть метода FtpFileDownload практически полностью соответствует более ранним версиям этого метода. В ней создается и отображается на экране стандартная диалоговая панель Save As. Эта панель позволяет пользователю указать имя файла и каталог на локальном компьютере в который записывается файл, загружаемый с сервера FTP. По умолчанию в качестве имени файла предлагается использовать имя загружаемого файла. Это имя передается конструктору класса CFileDialog через параметр sFileName:
// Создаем объект, управляющий панелью Save As
CFileDialog mFileOpen(FALSE, NULL, sFileName);
// Отображаем панель Save As на экране
int result = mFileOpen.DoModal();
После того как пользователь выберет файл и нажмет кнопку OK, или откажется от выбора и нажмет кнопку Cancel, диалоговая панель Save As закрывается и метод DoModal возвращает соответствующий код завершения.
Если пользователь отказался от выбора файла и нажал кнопку Cancel, то метод DoModal возвращает значение IDCANCEL. В этом случае мы только выводим соответствующее предупреждающее сообщение и завершаем работу метода FtpFileDownload не выполняя загрузки файла:
if(result == IDCANCEL)
{
AfxMessageBox("File not selected");
return FALSE;
}
Если пользователь выбрал файл и нажал кнопку OK (или выбрал файл, выполнив по его имени двойной щелчок левой кнопкой мыши), то метод DoModal возвращает значение IDOK. В этом случае мы определяем полный путь выбранного пользователем файла и записываем его в строку sLocalFileName:
CString sLocalFileName;
. . .
else if(result == IDOK)
{
sLocalFileName = mFileOpen.GetPathName();
}
Через единственный параметр метода FtpFileDownload ему передается строка sFileName, содержащая имя файла выбранного пользователем для загрузки с сервера. Чтобы получить полный путь этого файла на сервере мы добавляем имя файла к текущему каталогу. Результат записываем обратно в строку sFileName:
// Формируем полное имя файла для загрузки его с сервера FTP
sFileName = sCurentDirectory + "/" + sFileName;
Чтобы мы смогли определить во время загрузки файла какая его часть загружена, необходимо узнать размер файла. Для этой цели мы создаем объект класса CFtpFileFind и выполняем поиск данного файла на сервере.
Сначала мы вызываем метод FindFile, передавая ему в качестве параметра полный путь файла, который надо найти на сервере, а затем, в случае успеха, вызываем метод FindNextFile который позволит нам определить характеристики, и в том числе длину, обнаруженного файла.
Надо заметить, что файл sFileName обязательно будет обнаружен, так как мы уже нашли его раньше (см. метод DirectoryView). Конечно мы не учитываем в этом случае возможность разрыва связи, удаление файла администратором и сервера ит. д.:
// Создаем объект класса CFtpFileFind
CFtpFileFind m_FtpFileFind(m_FtpConnection);
// Выполняем поиск выбранного нами файла
if(fResult =
m_FtpFileFind.FindFile(_T(sFileName)))
{
// Если поиск закончился успешно, получаем его
// характеристики
fResult = m_FtpFileFind.FindNextFile();
. . .
}
Повторный поиск файла мы выполняем исключительно потому, что так наиболее просто узнать его размер. Для этого достаточно вызвать метод GetLength:
// Временная переменная для хранения размера файла
DWORD dwLength = 0;
// Определяем длину файла
dwLength = m_FtpFileFind.GetLength();
В соответствии с длинной загружаемого файла устанавливаются новые границы изменения значений для линейного индикатора и новый шаг приращения.
Мы будем загружать файл с сервера FTP блоками по READ_BLOCK байт в каждом (последний блок может иметь меньшую длину). Поэтому мы сможем изменить состояние линейного индикатора dwLength / READ_BLOCK раз:
// Устанавливаем новые границы для линейного индикатора
m_LoadProgress.SetRange(0, (int)(dwLength/READ_BLOCK) );
// Устанавливаем шаг приращения равный единице
m_LoadProgress.SetStep(1);
Заметим, что чем меньше загружаемый файл, тем более резко будет меняться значение линейного индикатора, отражающего процесс загрузки. Если размер загружаемого файла будет меньше, чем размер буфера (READ_BLOCK), то линейный индикатор вообще не будет задействован. В принципе, вы можете сделать линейный индикатор более чувствительным, если будете загружать файл меньшими порциями - уменьшите значение READ_BLOCK.
Перед тем как приступить к самому интересному - загрузке файла с сервера FTP, заканчиваем поиск и вызываем метод Close для объекта m_FtpFileFind:
// Так как мы искали только один файл, заканчиваем поиск
m_FtpFileFind.Close();
Перед тем как приступить к загрузке файла с сервера мы должны его открыть. Для этого следует воспользоваться методом OpenFile, входящим в состав класса CFtpConnection. Он возвращает указатель на объект класса CInternetFile:
// Определяем указатель на файл Internet
CInternetFile* iFile;
// Открываем выбранный нами файл на сервере FTP
iFile = m_FtpConnection -> OpenFile(
sFileName.GetBuffer(MIN_LEN_BUF),
GENERIC_READ,
FTP_TRANSFER_TYPE_BINARY
);
Первый параметр метода OpenFile задает имя файла, который надо открыть. Второй параметр выбирает режим в котором открывается файл. Константа GENERIC_READ означает, что файл открывается для чтения. Третий и последний параметр метода OpenFile устанавливает режим передачи двоичных данных. Мы выбрали этот режим чтобы иметь возможность загружать не только текстовые, но и двоичные файлы.
Файл, получаемый с сервера FTP, мы должны сохранить на локальном компьютере в файле под именем sLocalFileName. Создаем файл sLocalFileName, используя возможности класса CFile:
// Создаем и открываем файл на локальном диске компьютера
CFile fLocalFile(
sLocalFileName,
CFile::modeCreate | CFile::modeWrite
);
Файл создается с атрибутами CFile::modeCreate и CFile::modeWrite, поэтому если файла с именем sLocalFileName нет - он будет создан, а если такой файл уже есть, то он перезаписывается. У пользователя при этом запрашивается подтверждение на перезапись файла.
Для чтения информации из файла на сервере мы будем обращаться к методу Read класса CInternetFile. Этот метод считывает порцию данных из файла и записывает их во временный буфер. Буфер мы предварительно создаем, при помощи функции malloc:
// Создаем буфер для чтения файла с сервера FTP
void* ptrBuffer;
ptrBuffer = malloc( READ_BLOCK );
Затем в цикле мы приступаем к чтению данных из файла на сервере и записи их в файл на локальном компьютере. Цикл будет продолжаться до тех пор, пока не будет достигнут конец файла.
Операция чтения файла с сервера выполняется методом Read, определенным в классе CInternetFile. Каждый вызов метода Read считывает READ_BLOCK байт и записывает их в буфер ptrBuffer. Количество байт, которое действительно было считано из файла на сервере, возвращается методом Read и записывается во временную переменную nReadCount.
Как только после вызова метода Read значение nReadCount станет меньше READ_BLOCK значит достигнут конец файла и можно выходить из цикла чтения:
UINT nReadCount = 0;
do
{
. . .
// Читаем из файла на сервере READ_BLOCK байт
nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );
. . .
} while (nReadCount == READ_BLOCK);
Если во время чтения файла с сервера возникнет ошибка, например разорвется связь, то метод Read вызовет соответствующее исключение CInternetException. Мы организуем обработку такой ситуации и помещаем вызов метода Read в блок try, а ниже определяем блок catch:
try
{
// Читаем из файла на сервере READ_BLOCK байт
nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );
}
Блок catch выполняет обработку исключения CInternetException. Обращаясь к методу GetErrorMessage мы получаем текстовое описание причины, вызвавшей исключение и отображаем ее на экране. Если метод GetErrorMessage не может определить причину исключения, он возвращает нулевое значение и мы отображаем на экране строку GetFtpConnection Error:
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
AfxMessageBox(szErr);
else
AfxMessageBox("GetFtpConnection Error");
// Так как возникла ошибка, значит данные не считаны
nReadCount = 0;
// Удаляем исключение
pEx->Delete();
}
Чтобы прервать дальнейшее выполнение цикла загрузки файла мы записываем в переменную nReadCount нулевое значение. Затем удаляем исключение, вызывая для него метод Delete.
Данные, которые считаны из файла на сервере и находятся в буфере ptrBuffer, надо записать в файл fLocalFile на локальном диске компьютера. Для этого передаем буфер ptrBuffer методу Write и указываем, что надо записать в файл nReadCount байт. Напомним, что при достижении конца файла на сервере метод Read считывает остаток файла, размер которого может быть меньше размера буфера:
fLocalFile.Write( ptrBuffer, nReadCount );
Когда очередной блок из файла считан, увеличиваем значение линейного индикатора на одну позицию. За время загрузки файла линейный индикатор изменит свое значение от крайне левого до крайне правого:
// Увеличиваем значение на линейном индикаторе
m_LoadProgress.StepIt();
После того, как файл полностью получен, переводим линейный индикатор загрузки в начальное положение. Вызываем для этого метод SetPos с нулевым значением в качестве параметра:
// После окончания загрузки файла сбрасываем
// линейный индикатор
m_LoadProgress.SetPos( 0 );
Так как открытый файл на сервере FTP более нам не нужен, закрываем его, а затем удаляем из памяти соответствующий управляющий объект:
// Закрываем файл на сервере
iFile -> Close();
// Удаляем объект iFile
delete iFile;
Также закрываем и локальный файл, в который скопирован файл с сервера FTP:
// Закрываем файл на локальном компьютере
fLocalFile.Close();
Освобождаем буфер ptrBuffer, вызывая функцию free:
// Освобождаем буфер ptrBuffer
free( ptrBuffer );
Метод FtpFileDownload
Метод FtpFileDownload имеет единственный параметр, через который передается имя файла, выбранного пользователем из списка объектов сервера FTP:BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
. . .
}
Для начала метод FtpFileDownload запрашивает у пользователя имя файла, под которым файл с сервера FTP будет записан на диске локального компьютера.
Для облегчения этой задачи мы воспользовались классом CFileDialog, который входит в состав библиотеки классов MFC и предназначен для управления стандартной диалоговой панелью Save As. Объявляем объект mFileOpen этого класса:
CFileDialog mFileOpen(FALSE, NULL, sFileName);
¨ Класс CFileDialog, а также некоторые другие классы стандартных диалоговых панелей были описаны нами в 28 томе серии “Библиотека системного программиста”.
Конструктору класса CFileDialog мы указали три параметра. Первый параметр имеет значение FALSE и определяет, что объект mFileOpen будет управлять диалоговой панелью Save As. Если бы мы указали вместо FALSE значение TRUE, то была бы создана диалоговая панель Open.
Второй параметр конструктора может задавать расширение для имен файлов, которое будет выбрано по умолчанию, но мы не используем эту возможность, так как наше приложение способно загружать с сервера FTP файлы различных типов с разными расширениями.
Третий параметр конструктора sFileName содержит имя файла, которое мы выбрали из списка. Это имя будет предложено в диалоговой панели Save As по умолчанию.
Конструктор класса CFileDialog не открывает диалоговой панели Save As. Он только создает управляющий объект mFileOpen. Чтобы диалоговая панель Save As появилась на экране вызывается метод DoModal класса CFileDialog:
result = mFileOpen.DoModal();
Метод DoModal создает модальную диалоговую панель. Поэтому он возвращает управление только тогда, когда пользователь выберет файл или откажется от выбора и нажмет кнопку Cancel.
Если пользователь откажется от выбора и нажмет кнопку Cancel, то метод DoModal вернет значение IDCANCEL. Тогда мы выводим на экран соответствующее сообщение и завершаем метод FtpFileDownload, возвращая значение FALSE:
// Проверяем как была закрыта диалоговая панель Open -
// по нажатию кнопки OK или Cancel
if(result == IDCANCEL)
{
// Если пользователь отказался от выбора файлов и
// нажал кнопку Cancel отображаем соответствующее
// сообщение и возвращаем значение FALSE
AfxMessageBox("File not selected");
return FALSE;
}
Если пользователь выбрал файл из диалоговой панели Save As, то метод DoModal возвращает значение IDOK. В этом случае мы определяем полный путь файла, включая имя диска, путь каталога и имя файла и записываем его во временную строку sLocalFileName. Эта строка определена в данном метода как объект класса CString:
else if(result == IDOK)
{
sLocalFileName = mFileOpen.GetPathName();
}
Через параметр sFileName методу FtpFileDownload было передано имя файла, выбранного пользователем из списка файлов и каталогов сервера FTP. Мы не стали передавать полный путь файла целиком, так как имя файла можно использовать для инициализации диалоговой панели Save As. Текущий путь каталога сервера FTP записан в строке sCurentDirectory, поэтому чтобы сформировать полный путь файла, выбранного на сервере, достаточно выполнить следующую операцию:
sFileName = sCurentDirectory + "/" + sFileName;
Теперь в строке sFileName записан полный путь файла, расположенного на сервере FTP, а в строке sLocalFileName - полный путь файла на диске локального компьютера.
Остается загрузить файл с сервера FTP. Для этого мы вызываем метод GetFile, входящий в класс CFtpConnection. Метод GetFile имеет много различных параметров, но в нашем приложении мы используем только три. Первый параметр задает полный путь файла, который надо получить с сервера FTP, второй - полный путь файла на локальном компьютере в который полученный файл будет записан, а третий - определяет поведение метода в том случае если второй параметр задает файл, уже существующий на диске. В данном случае в качестве третьего параметра используется значение FALSE. Оно означает, что если на диске локального компьютера уже есть файл с таким именем, он перезаписывается (предварительно у пользователя запрашивается подтверждение на перезапись файла). Вы можете использовать в качестве третьего параметра значение TRUE или вовсе его не указывать. Тогда метод GetFile не будет перезаписывать уже существующий файл, а просто завершится с ошибкой:
fResult = m_FtpConnection ->
GetFile( sFileName.GetBuffer(MIN_LEN_BUF),
sLocalFileName.GetBuffer(MIN_LEN_BUF),
FALSE
);
В случае успешного завершения метод GetFile возвращает ненулевое значение, а если произошла какая-нибудь ошибка - нуль. Мы используем значение, возвращаемое методом GetFile в качестве результата работы самого метода FtpFileDownload:
return fResult;
Метод GetCurrentDirectory
Метод GetCurrentDirectory позволяет определить имя текущего каталога на сервере FTP. В состав класса CFtpConnection входят два метода GetCurrentDirectory, имеющие различный набор параметров.Прототип метода GetCurrentDirectory с одним параметром представлен ниже. Как видите, этому методу передается ссылка на строку strDirName. В нее метод GetCurrentDirectory записывает имя текущего каталога сервера:
BOOL GetCurrentDirectory( CString& strDirName ) const;
В случае успешного завершения метод GetCurrentDirectory возвращает ненулевое значение и ноль в случае ошибки. Чтобы определить причину возникшей ошибки можно воспользоваться функцией GetLastError.
Другой метод GetCurrentDirectory имеет два параметра. Параметр pstrDirName должен содержать указатель на буфер, в который будет записано имя текущего каталога. Размер этого буфера вы должны записать в переменную типа DWORD и передать указатель на него через параметр lpdwLen:
BOOL GetCurrentDirectory(
LPTSTR pstrDirName,
LPDWORD lpdwLen
) const;
В случае успешного завершения метод GetCurrentDirectory возвращает ненулевое значение, а в переменную по адресу lpdwLen записывает длину строки с именем каталога.
Метод GetCurrentDirectoryAsURL
В класс CFtpConnection входят два метода GetCurrentDirectoryAsURL, различающиеся своими парамтерами, которые позволяют определить текущий каталог на сервере FTP в формате URL. Прототипы этих методов мы привели ниже:BOOL GetCurrentDirectoryAsURL( CString& strDirName ) const;
BOOL GetCurrentDirectoryAsURL(
LPTSTR pstrDirName, LPDWORD lpdwLen
) const;
Назначение параметров представленных выше методов GetCurrentDirectoryAsURL полностью соответствует уже описанным нами методам GetCurrentDirectory, за исключением того, что эти методы определяют текущий каталог в формате URL.
Метод GetFile
Метод GetFile копирует файл с сервера FTP на диск локального компьютера. При этом вы фактически не имеете никакой возможности следить за процессом копирования. Если вы должны знать, какая часть файла скопирована в данный момент, то вместо метода GetFile надо использовать пару методов - метод OpenFile класса CFtpConnection и метод Read класса CInternetFile. Сначала надо открыть файл на сервере FTP при помощи метода OpenFile, а затем считывать из него данные с помощью метода Read. Мы продемонстрируем оба этих метода получения файла с сервера FTP в разделе “Загрузка файлов с сервера FTP”.Ниже мы привели прототип метода GetFile:
BOOL GetFile(
LPCTSTR pstrRemoteFile,
LPCTSTR pstrLocalFile,
BOOL bFailIfExists = TRUE,
DWORD dwAttributes = FILE_ATTRIBUTE_NORMAL,
DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY,
DWORD dwContext = 1
);
Имя файла на сервере, который надо получить, передается методу GetFile через параметр pstrRemoteFile, а имя файла на диске локального компьютера - через параметр pstrLocalFile. Вы должны обязательно задать только эти два параметра. Остальные параметры имеют значения, принимаемые по умолчанию, и могут не указываться.
Параметр bFailIfExists определяет действие метода GetFile в случае, если в качестве параметра pstrLocalFile вы указали имя файла, который уже существует на диске локального компьютера. Если вы укажете в качестве параметра bFailIfExists значение TRUE или опустите этот параметр, тогда метод GetFile завершится с ошибкой. Если же параметр bFailIfExists равен FALSE то файл на локальном диске будет перезаписан. Предварительно у пользователя запрашивается разрешение на перезапись файла.
Метод GetFile позволяет выбрать атрибуты для файла, который создается на локальном диске компьютера. Эти атрибуты передаются методу через параметр dwAttributes.
| Атрибут | Описание | ||
| FILE_ATTRIBUTE_NORMAL | Нормальный файл | ||
| FILE_ATTRIBUTE_READONLY | Файл, который можно открыть только для чтения | ||
| FILE_ATTRIBUTE_HIDDEN | Скрытый файл | ||
| FILE_ATTRIBUTE_SYSTEM | Системный файл | ||
| FILE_ATTRIBUTE_TEMPORARY | Временный файл | ||
| FILE_ATTRIBUTE_COMPRESSED | Компрессованый файл | ||
| FILE_ATTRIBUTE_DIRECTORY | Каталог | ||
| FILE_ATTRIBUTE_ARCHIVE | Архивный файл |
В случае успешного завершения метод GetFile возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод передачи данных с сервера FTP на локальный компьютер можно задавать через параметр dwFlags. Если вам надо передать двоичный файл без всяких перекодировок, укажите в качестве этого параметра значение FTP_TRANSFER_TYPE_BINARY (метод FTP Image - тип I).
Если вам надо принять только текстовые данные, то вы можете использовать для этого параметра значение FTP_TRANSFER_TYPE_ASCII. При этом выполняется перекодировка данных в зависимости от программного обеспечения, установленного на сервере (метод FTP ASCII - тип A).
Идентификатор контекста для операции загруки файла можно указать в параметре dwContext. По умолчанию в качестве идентификатора контекста используется значение 1.
Метод GetFileURL
Метод GetFileURL возвращает имя файла в формате URL. Вы можете использовать данный метод после передачи запроса серверу с помощью метода SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.Прототип метода GetFileURL представлен ниже:
virtual CString GetFileURL( ) const;
Обнаружив файл при помощи метода FindNextFile, вы можете определить его адрес в формате URL, вызвав виртуальный метод GetFileURL:
virtual CString GetFileURL( ) const;
Метод GetFileURL возвращает адрес файла в строке класса CString.
Если в качестве имени файла для поиска вы указали шаблон, то чтобы узнать имя обнаруженного объекта - файла или каталога вы можете воспользоваться методом GetFileURL класса CFtpFileFind:
CString GetFileURL( ) const;
Он возвращает адрес обнаруженного объекта в формате URL. Для определения имени объекта вы также можете воспользоваться некоторыми методами базового класса CFileFind.
Метод GetFtpConnection
Метод GetFtpConnection выполняет соединение с заданным сервером FTP. При этом он создает соответствующий объект класса CFTPConnection, который будет представлять соединение с данным сервером. Это, кстати, единственный способ, который рекомендуется использовать для создания объектов класса CFtpConnection. Нельзя создавать объекты этого класса, напрямую обращаясь к его конструктору.После того как вы соединились с сервером FTP и объект класса CFtpConnection создан, вы можете вызывать для него методы этого класса чтобы выполнять различные операции с сервером. Так, например, при помощи метода OpenFile вы можете открыть файл, расположенный на сервере FTP для последующего чтения или записи.
Метод GetFtpConnection имеет множество различных параметров. Однако только первый параметр вы должны будете указать в обязательном порядке. Остальные параметры можно опустить. В этом случае для них будут использоваться значения, принятые по умолчанию, которые подходят в большинстве случаев. Ниже представлен прототип метода GetFtpConnection:
CFtpConnection*
GetFtpConnection(
LPCTSTR pstrServer,
LPCTSTR pstrUserName = NULL,
LPCTSTR pstrPassword = NULL,
INTERNET_PORT nPort = INTERNET_INVALID_PORT_NUMBER,
BOOL bPassive = FALSE
);
throw( CInternetException );
Как мы уже говорили, метод GetFtpConnection создает объект класса CFtpConnection и возвращает указатель на него. Если в ходе соединения с сервером FTP возникнут какие-либо проблемы, например сервера FTP, который вы указали, вообще не существует в сети или он не активен в настоящее время, тогда метод GetFtpConnection может вызвать исключение CInternetException. Обработав данное исключение, вы сможете определить причины ошибки.
Рассмотрим теперь более подробно параметры метода.
Наиболее важный параметр pstrServer. Через него вы передаете методу GetFtpConnection адрес сервера, с которым необходимо установить соединение. Вполне естественно, что этот параметр является обязательным (конечно, корпорация Microsoft могла бы предусмотреть и для этого параметра значение по умолчанию, например ftp://ftp.microsoft.com).
Следующие два параметра pstrUserName и pstrPassword задают имя и пароль, под которым выполняется соединение с сервером FTP. Если вы не укажете имя, то по умолчанию используется anonymous. Если имя не указано, то пароль также не должен задаваться. В этом случае в качестве пароля для соединения с сервером WinInet будет использовать адрес вашей электронной почты.
Заметим, что если вы указали имя lpszUsername, то это не означает, что вы должны обязательно задавать и пароль. В случае если имя задано, а пароль нет (через параметр lpszPassword передается значение NULL или параметр не используется совсем), то в качестве пароля используется пустая строка.
Следующий параметр метода GetFtpConnection - nPort. Он определяет номер порта TCP/IP который используется сервером FTP для соединения. По умолчанию в качестве этого параметра используется значение INTERNET_INVALID_PORT_NUMBER. В этом случае метод GetFtpConnection использует при соединении с сервером FTP стандартный для него порт номер 21.
Последний параметр метода - bPassive определяет режим работы с сервером FTP. Если параметр bPassive содержит значение TRUE, то выполняется пассивное соединение с сервером.
Метод GetHttpConnection
Метод GetHttpConnection имеет сходное название с методом GetFtpConnection, и служит для соединения с сервером WWW. Для этого он создает объект класса CHttpConnection, который будет представлять соединение с сервером WWW.Заметим, что в отличие от ранее рассмотренного метода GetFtpConnection, метод GetHttpConnection не выполняет соединения с сервером в полном смысле этого слова. Физическое соединение в сети не устанавливается. Отсюда, кстати следует, что даже если вы укажите данному методу адрес несуществующего сервера WWW, он не заметит подвоха, послушно установит “соединение” и создаст объект CHttpConnection.
Фактическое соединение с сервером WWW произойдет несколько позже, когда вы будете передавать запросы серверу, используя методы класса CHttpConnection.
Ниже мы привели прототип метода GetHttpConnection:
CHttpConnection*
GetHttpConnection(
LPCTSTR pstrServer,
INTERNET_PORT nPort = INTERNET_INVALID_PORT_NUMBER,
LPCTSTR pstrUserName = NULL,
LPCTSTR pstrPassword = NULL
);
throw( CInternetException );
Только первый параметр метода GetHttpConnection является обязательным. Он задает адрес сервера WWW с которым надо соединиться. Второй параметр - nPort - задает имя порта TCP/IP, который используется сервером WWW для соединения со своими клиентами.
По умолчанию в качестве параметра nPort используется значение INTERNET_INVALID_PORT_NUMBER. В этом случае метод GetHttpConnection использует порт номер 80, стандартный для серверов WWW.
Последние два параметра метода GetHttpConnection - pstrUserName и pstrPassword задают имя пользователя и пароль, под которым ваше приложение будет соединятся с сервером WWW.
Метод GetHttpConnection создает объект класса CHttpConnection и возвращает указатель на него. В случае возникновения ошибок, метод GetHttpConnection может вызвать исключение CInternetException, которое вы должны обрабатывать в своем приложении.
Метод GetLastWriteTime
В состав класса CFileFind входят несколько методов, которые определяют различные временные характеристики файла. Вы можете узнать время создания файла, время последнего обращения к файлу и время последнего изменения файла. Для этих целей предназначены методы GetCreationTime, GetLastAccessTime и GetLastWriteTime. Они имеют сходный формат, поэтому мы рассмотрим только метод GetLastWriteTime, остальные методы вы изучите самостоятельно.Определены два варианта метода GetLastWriteTime, которые используют для своей работы либо традиционную структуру FILETIME, либо более соответствующий концепции библиотеки MFC объект класса CTime:
virtual BOOL GetLastWriteTime( FILETIME* pFileTime ) const;
virtual BOOL GetLastWriteTime( CTime& refTime ) const;
В случае успешного завершения метод GetLastWriteTime возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль.
Метод GetLength
После того, как вы нашли файл при помощи метода FindNextFile, вы можете определить его длину. Для этого следует воспользоваться методом GetLength, который возвращает длину файла в байтах:DWORD GetLength( ) const;
Метод IsDirectory
Метод FindNextFile позволяет обнаружить не только файлы, но также и каталоги. Метод IsDirectory определяет какой объект получен методом FindNextFile - файл или каталог:BOOL IsDirectory( ) const;
Если объект является каталогом, тогда метод IsDirectory возвращает значение TRUE, а если файлом - тогда FALSE.
Метод OnButtonBack
Просмотрев несколько страниц WWW, пользователь может вернуться к уже просмотренным страницам, если нажмет кнопку Back. В этом случае вызывается метод OnButtonBack класса CLookDlg, который возвращает навигатор к просмотру предыдущих страниц:m_explorer.GoBack();
Метод OnButtonNext
Если вы перешли к уже просмотренным страницам, нажав кнопку Back, вы можете вернуться обратно, если нажмете кнопку Next. В ответ на это вызывается метод OnButtonNext класса CLookDlg, который дает соответствующую команду навигатору:m_explorer.GoForward();
Метод OnButtonRefresh
Когда пользователь нажимает на кнопку Refresh, вызывается метод OnButtonRefresh класса CLookDlg. Этот метод обновляет текущую страницу, отображаемую в данный момент навигатором. Для этого используется метод Refresh:m_explorer.Refresh();
Метод OnButtonStop
Пользователь может прервать загрузку страницы WWW, если нажмет кнопку Stop. В этом случае вызывается метод OnButtonStop останавливающий загрузку при помощи метода Stop:m_explorer.Stop();
Метод OnConnect
Метод OnConnect класса CFtpViewDlg вызывается, когда вы нажимаете кнопку Connect чтобы соединится с сервером FTP. Адрес сервера FTP, с которым устанавливается соединение, должен быть введен в поле редактирования IDC_FTP_ADDRESS. Это поле располагается на диалоговой панели с левой стороны от кнопки Connect.Перед тем как устанавливать соединение с сервером FTP, метод OnConnect блокирует кнопки управления, расположенные на диалоговой панели - Connect, OK и On top. Блокировка выполняется с помощью метода EnableWindow, определенного в классе CWnd. Этот метод вызывается для объектов m_Connect, m_Ok и m_OnTop класса CButton, представляющих эти кнопки:
// Блокируем кнопки Connect, OK и On Top
m_Connect.EnableWindow(FALSE);
m_Ok.EnableWindow(FALSE);
m_OnTop.EnableWindow(FALSE);
Если вы ранее уже соединились с сервером FTP и указатель m_FtpConnection не равен значению NULL, разрываем эту связь и удаляем объект m_FtpConnection. Затем присваиваем m_FtpConnection значение NULL:
if (m_FtpConnection != NULL)
{
m_FtpConnection -> Close();
delete m_FtpConnection;
m_FtpConnection = NULL;
}
Пользователь мог изменить адрес сервера, с которым надо установить соединение, поэтому обновляем строку m_FtpAddress, считывая значение из поля редактирования IDC_FTP_ADDRESS. Для этого опять (раньше в методе OnInitDialog) вызываем метод UpdateData, но теперь указываем ему параметр TRUE. Он означает, что состояние органов управления диалоговой панели должно быть записано в привязанные к ним переменные:
UpdateData(TRUE);
Теперь, когда адрес сервера известен, пытаемся с ним соединиться. Для этого вызываем метод GetFtpConnection класса CInternetSession объекта. В случае ошибки метод GetFtpConnection может вызвать исключение CInternetException.
Чтобы организовать обработку этого исключения помещаем вызов метода GetFtpConnection в блок try и создаем соответствующий блок catch:
// Пытаемся соединиться с сервером FTP
try
{
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Соединяемся с сервером FTP. Эта операция
// может вызвать исключение CInternetException
m_FtpConnection =
m_InternetSession->GetFtpConnection(m_FtpAddress);
}
Так как процесс соединения с сервером может занять достаточно много времени, изменяем форму курсора. Для этого достаточно создать объект wait класса CWaitCursor. Курсор примет свою прежнюю форму автоматически, когда объект wait будет удален. В данном случае это произойдет при выходе из блока try.
В случае если работа метода GetFtpConnection вызовет исключение CInternetException, вызывается соответствующий блок catch. Такое может произойти, например, если сервер FTP с заданным адресом не существует или он не работает в данное время.
Обработчик исключения вызывает для него метод GetErrorMessage. Он возвращает текстовое описание причины исключения, которое затем отображается на экране с помощью функции AfxMessageBox. Если метод GetErrorMessage не может вернуть текстового описания, то на экране отображается сообщение GetFtpConnection Error.
Чтобы завершить обработку исключения удаляем его, вызывая для исключения метод Delete. Так как исключение означает, что установить соединение с FTP сервером не удалось, присваиваем указателю m_FtpConnection значение NULL:
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
AfxMessageBox(szErr);
else
AfxMessageBox("GetFtpConnection Error");
// Удаляем иссключение
pEx->Delete();
// Обнуляем указатель m_FtpConnection
m_FtpConnection = NULL;
}
Если соединение не установлено, тогда выводим соответствующую надпись в поле IDC_STATUS, снимаем блокировку кнопок Connect, OK и On Top и завершаем работу метода. Чтобы снять блокировку кнопок, вызываем для управляющих ими объектов метод EnableWindow с параметром TRUE:
if( m_FtpConnection == NULL )
m_Status.SetWindowText("Connect not established");
...
m_Connect.EnableWindow(TRUE);
m_Ok.EnableWindow(TRUE);
m_OnTop.EnableWindow(TRUE);
Если соединение с сервером FTP установлено успешно, определяем текущий каталог. Для этого вызываем метод GetCurrentDirectory, передавая ему в качестве параметра строку sCurrentFtpDirectory. Эта строка определена в методе OnConnect как объект класса CString:
// Определяем текущий каталог сервера FTP
BOOL fResult=
m_FtpConnection ->
GetCurrentDirectory(sCurrentFtpDirectory);
if(fResult)
sCurentDirectory = sCurrentFtpDirectory;
else
AfxMessageBox("GetCurrentDirectory Error");
В случае успешного завершения метод GetCurrentDirectory запишет в строку sCurrentFtpDirectory полный путь текущего каталога и мы скопируем его в строку sCurentDirectory, являющуюся элементом класса CFtpViewDlg.
Узнав текущий каталог, вызываем метод DirectoryView, определенный в классе CFtpViewDlg, который считывает имена файлов и каталогов сервера FTP и отображает их в списке на диалоговой панели. Перед вызовом метода DirectoryView мы создаем объект класса CWaitCursor, поэтому во время длительного процесса опроса текущего каталога сервера курсор изменит свою форму. Заметим, что после заполнения списка при выходе управления из блока в котором определен объект wait класса CWaitCursor, форма курсора автоматически восстанавливается:
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое выбранного каталога
DirectoryView();
После того, как содержимое каталога выведено в списке на диалоговой панели, отображаем в поле IDC_STATUS путь каталога и снимаем блокировку с кнопок Connect, OK и On Top:
// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
Метод OnDblclkFtpList
В списке IDC_FTP_LIST показываются имена файлов и каталогов сервера FTP. Файлы выделяются пиктограммой
, а каталоги пиктограммой
и строкой Dir в столбце Length. Чтобы вы имели возможность просмотра не только того каталога, с которым вас по умолчанию соединил сервер FTP, но также и других вложенных каталогов, мы предусмотрели метод OnDblclkFtpList. ¨ Наше приложение не меняет текущий каталог сервера FTP. Для этого предназначен метод SetCurrentDirectory класса CFtpConnection. Изменяется только каталог, в котором осуществляется поиск файлов и других каталогов
Метод OnDblclkFtpList вызывается когда пользователь выполняет двойной щелчок левой клавишей мыши по имени каталога или имени файла. В качестве параметров методу OnDblclkFtpList передаются указатель pNMHDR на структуру типа NMHDR и указатель pResult на переменную типа LRESULT.
Рассмотрим метод OnDblclkFtpList более подробно. Сначала он блокирует список IDC_FTP_LIST, вызывая метод EnableWindow с параметром FALSE:
CString sSelItem; // Название каталога
// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);
Затем с помощью метода GetItemCount мы определяем количество элементов в списке IDC_FTP_LIST и записываем его во временную переменную iTotalNumber типа int:
iTotalNumber = m_FtpList.GetItemCount();
Далее мы пытаемся определить элемент, который выбран из списка. К сожалению мы не обнаружили простого способа выполнить эту задачу. Поэтому последовательно проверяем все элементы списка до тех пор, пока не найдем элемент, у которого установлен атрибут LVIS_SELECTED, означающий, что он выбран.
Для определения атрибутов элемента списка вызываем метод GetItemState. В качестве первого параметра методу GetItemState указывается индекс элемента, атрибуты которого надо определить, а в качестве второго - комбинацию атрибутов состояние которых нужно узнать. В нашем случае в качестве второго параметра фигурирует только константа LVIS_SELECTED, так как нас интересует только выбран данный элемент или нет:
// Определяем, какой объект списка выбран
for(int i = 0; i < iTotalNumber; i++)
{
if(LVIS_SELECTED == m_FtpList.GetItemState(i,LVIS_SELECTED))
{
. . .
break;
}
}
Если метод m_FtpList.GetItemState( i, LVIS_SELECTED )) возвращает значение LVIS_SELECTED, значит элемент с индексом i выбран из списка. В этом случае определяем имя соответствующего объекта и его размер, считывая их непосредственно из первой и второй колонки списка:
// Определяем название выбранного элемента списка
// (имя файла или каталога)
sSelItem = m_FtpList.GetItemText( i, 0 );
// Считываем данные из колонки Length
sLength_Dir = m_FtpList.GetItemText( i, 1 );
Если вместо длины файла во второй колонке записана строка Dir, значит из списка выбран каталог. Тогда мы добавляем название этого каталога к концу строки, содержащей текущий каталог сервера FTP.
В противном случае вы выбрали из списка не каталог, а файл и на экран выводится предупреждающее сообщение, о том что вы выбрали файл с определенным именем:
if(sLength_Dir == "Dir") // Выбран каталог
sCurentDirectory = sCurentDirectory + "/" + sSelItem;
else // Выбран файл
AfxMessageBox("You select file " + sSelItem);
После этого изменяем форму курсора и вызываем метод DirectoryView, который считывает имена объектов, расположенных в каталоге sCurentDirectory и отображает их в списке диалоговой панели приложения.
Чтобы изменить форму курсора мы создаем объект wait класса CWaitCursor. Когда при выходе из метода OnDblclkFtpList этот объект удаляется, курсор автоматически восстанавливает свою форму:
. . .
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое выбранного каталога
DirectoryView();
}
Затем в поле IDC_STATUS отображается новый путь каталога и снимается блокировка со списка, так что мы опять можем выбирать из него файлы и каталоги:
// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);
В завершении метода OnDblclkFtpList мы записываем по адресу pResult нулевое значение, которое означает, что метод успешно завершен:
*pResult = 0;
Метод OnDownloadBeginExplorer
Когда навигатор приступает к загрузке очередного объекта с сервера, будь то сама страница HTML, рисунок, анимация или что либо еще, вызывается метод OnDownloadBeginExplorer.В этом случае мы инициализируем линейный индикатор, который будет показывать ход загрузки данного объекта:
m_Progress.SetRange(0, 100);
m_Progress.SetStep(0);
m_Progress.SetPos(0);
Метод OnDownloadCompleteExplorer
По окончании загрузки очередного объекта, навигатор вызывает метод OnDownloadCompleteExplorer. В нем мы сбрасываем линейный индикатор в начальное положение:m_Progress.SetPos(0);
Метод OnInitDialog
После того, как метод InitInstance главного класса приложения CFtpViewApp создает объект dlg класса CFtpViewDlg, представляющий диалоговую панель, для него вызывается метод DoModal:int nResponse = dlg.DoModal();
Этот метод кроме прочего передает диалоговой панели сообщение WM_INITDIALOG. В ответ на сообщение WM_INITDIALOG вызывается метод OnInitDialog, объявленный как виртуальный метод класса CDialog. Обратите внимание, что таблица сообщений класса CFtpViewDlg не содержит макрокоманд для обработки сообщения WM_INITDIALOG. Метод OnInitDialog вызывается непосредственно MFC.
Первоначально метод OnInitDialog был переопределен MFC AppWizard во время создания приложения. Затем мы его изменили, добавив программный код для инициализации органов управления диалоговой панели и инициализации сеанса работы с WinInet. Рассмотрим метод OnInitDialog более подробно.
Сначала вызывается метод OnInitDialog базового класса CDialog. Он выполняет основную работу по инициализации диалоговой панели - загружает соответствующий шаблон диалоговой панели из ресурсов приложения и т. д.:
CDialog::OnInitDialog();
Затем два раза вызывается метод SetIcon, определенный в базовом классе CWnd. Этот метод устанавливает пиктограммы стандартного и уменьшенного размера, которые будут отображаться в случае минимизации диалоговой панели:
SetIcon(m_hIcon,TRUE); // Пиктограмма стандартного размера
SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера
Первый параметр m_hIcon, передаваемый методам SetIcon, представляет собой индекс пиктограммы IDR_MAINFRAME. Эта пиктограмма загружается конструктором класса CFtpViewDlg, описанным выше.
Далее метод OnInitDialog выполняет инициализацию списка IDC_FTP_LIST, в котором будет отображаться содержимое каталогов сервера FTP.
Для временного использования объявляются несколько переменных - объект rectList класса CRect, структура lv_column типа LV_COLUMN и список строк szColHeader:
// Структура, для описания характеристик колонок списка
LV_COLUMN lv_column;
// Переменная для определения размера списка
CRect rectList;
// Названия для колонок списка
TCHAR szColHeader[COL_NUM][10] = {
_T("Name"),
_T("Length"),
_T("Date"),
_T("Time"),
};
В переменную rectList записывается размер области диалоговой панели, которую занимает список IDC_FTP_LIST. Для этого мы вызываем метод GetWindowRect объекта, управляющего списком. Размеры списка нам потребуются, когда мы будем определять ширину колонок, входящих в список:
m_FtpList.GetWindowRect(&rectList);
Затем мы заполняем поля структуры lv_column. Эта структура определяет характеристики колонки списка. Мы будем ее использовать при создании колонок.
Сначала заполняется поле mask. В него заносится комбинация нескольких констант, определяющих какие поля данной структуры будут иметь значение. Мы указали, что используются четыре поля структуры lv_column - fmt, iSubItem, pszText и cx:
lv_column.mask = LVCF_FMT | // Используется поле fmt
LVCF_SUBITEM | // Используется поле iSubItem
LVCF_TEXT | // Используется поле pszText
LVCF_WIDTH; // Используется поле cx
Поле fmt задает выравнивание по левому краю колонки ее заголовка и текста элементов. Мы выбрали выравнивание по левому краю, записав в это поле значение LVCFMT_LEFT:
lv_column.fmt = LVCFMT_LEFT;
В поле cx заносится ширина колонки. Мы указываем для всех колонок списка одинаковую ширину, разделив на равные части ширину всего списка:
lv_column.cx = (rectList.Width() / COL_NUM) - 1;
Остальные поля структуры lv_column - iSubItem и pszText различаются для каждой колонки. Поэтому мы заполняем их отдельно. Поле iSubItem означает номер колонки списка, а поле pszText - текст его заголовка.
В следующем цикле заполняются последние два поля структуры и метод InsertColumn включает колонку в список. Номера колонок изменяются от 0 до 3, а названия полей берутся из списка строк szColHeader:
// Определяем характеристики колонок списка
for (int i = 0; i < COL_NUM; i++)
{
// Номер колонки
lv_column.iSubItem = i;
// Зааголовок колонки
lv_column.pszText = szColHeader[i];
// Добавляем колонку с заданными свойствами к списку
m_FtpList.InsertColumn(i, &lv_column);
}
Новые колонки добавляются в список с помощью метода InsertColumn, определенного в классе CListCtrl. Этому методу передается номер колонки и заполненная структура lv_column, описывающая добавляемую колонку.
В списке сервера FTP могут фигурировать два типа объектов - каталоги и файлы. Для выделения их в списке им присваиваются различные пиктограммы.
Чтобы в списке можно было отмечать отдельные элементы при помощи пиктограмм, необходимо сформировать список изображений. Для этого в состав библиотеки MFC включен класс CImageList. Метод OnInitDialog создает объект этого класса, представляющий список из двух изображений размером 16 х 16 пикселов и включает в него пиктограммы с изображением каталога (пиктограмма с идентификатором IDI_DIRECTORY) и файла (пиктограмма с идентификатором IDI_FILE):
// Создаем список из двух изображений размера 16 х 16
m_ImageList = new CImageList();
m_ImageList -> Create(16, 16, TRUE, 2, 2);
// Добавляем в список две пиктограммы -
// IDI_DIRECTORY (изображение каталога) и
// IDI_FILE(изображение файла )
m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_DIRECTORY));
m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_FILE));
Полученный список изображений выбирается для использования в списке IDC_FTP_LIST с помощью метода SetImageList класса CListCtrl. Первый параметр метода SetImageList содержит указатель на список изображений, а второй определяет их размер. Константа LVSIL_SMALL означает, что данный список изображений будет использоваться при отображении пиктограмм маленького размера:
m_FtpList.SetImageList(m_ImageList, LVSIL_SMALL);
По завершении инициализации списка IDC_FTP_LIST, метод OnInitDialog выводит в поле редактирования IDC_FTP_ADDRESS, адрес сервера FTP. Адрес сервера FTP, который будет использоваться по умолчанию записывается в элемент данных m_FtpAddress класса CFtpViewDlg:
m_FtpAddress = "dials.ccas.ru"; // Сервер FTP “ДиалогНаука”
Для нашего примера мы использовали сервер FTP АО “ДиалогНаука”, который имеет адрес ftp://dials.ccas.ru. Вы можете заменить этот адрес на адрес любого другого сервера FTP по своему усмотрению.
Поле редактирования, предназначенное для ввода адреса сервера, мы связали со строкой m_FtpAddress. Поэтому чтобы вывести ее на экран вызывается метод UpdateData и в качестве параметра ему указывается значение FALSE:
UpdateData(FALSE);
Далее метод OnInitDialog приступает к инициализации сеанса связи с Internet (программного интерфейса WinInet). Создается новый объект класса CInternetSession. В качестве параметров конструктору класса CInternetSession указывается только имя приложения - строка FtpView. Вы можете указать здесь любую другую строку или не использовать этот параметр совсем. Указатель на созданный объект заносится в элемент данных m_InternetSession, принадлежащий классу CFtpViewDlg:
// Создаем сеанс связи с Internet, указываем в качестве
// имени программы-клиента название приложения FtpView
m_InternetSession = new CInternetSession("FtpView");
В случае возникновения ошибки при создании объекта класса CInternetSession мы отображаем на экране соответствующее сообщение и завершаем работу приложения:
if(!m_InternetSession)
{
AfxMessageBox("New Session Error", MB_OK);
OnOK();
}
Если объект класса CInternetSession создан, инициализируем указатель m_FtpConnection, также принадлежащий классу CFtpViewDlg:
m_FtpConnection = NULL;
После того как приложение установит связь с сервером FTP, адрес объекта класса CFtpConnection, представляющего сеанс связи с эти сервером, будет занесен в элемент данных m_FtpConnection.
Метод OnInitDialog возвращает значение TRUE. Это означает, что фокус ввода будет установлен на первый орган управления диалоговой панели (первый орган диалоговой панели можно выбрать в редакторе диалоговой панели, выбрав из меню Layout строку Tab Order).
По завершении работы диалоговая панель отображается на экране и пользователь может с ней работать - изменять адрес сервера FTP, соединятся с ним и просматривать содержимое его каталогов.
Метод OnNavigate
После запуска приложения навигатор не задействован. Чтобы он начал работать, пользователь должен выбрать из списка адрес сервера, а затем нажать кнопку Navigate. Когда пользователь нажимает эту кнопку, вызывается метод OnNavigate класса CLookDlg.В нем мы сначала определяем адрес, выбранный из списка. Для этого мы вызываем метод UpdateData с параметром TRUE. Он копирует строку выбранную из списка IDC_COMBO_ADDRESS в строку m_address:
UpdateData(TRUE);
Этот адрес передается навигатору, он соединяется с указанным сервером и отображает соответствующую страницу в своем окне:
m_explorer.Navigate(
m_address,
NULL, NULL, NULL, NULL
);
Метод OnNavigateCompleteExplorer
После того, как навигатор завершит загрузку страницы WWW он вызывает метод OnNavigateCompleteExplorer и передает ему точный адрес этой страницы:void CLookDlg::OnNavigateCompleteExplorer(LPCTSTR URL)
{
}
Мы записываем этот адрес в строку m_address и отображаем его в диалоговой панели, для чего вызываем метод UpdateData с параметром FALSE:
m_address = URL;
UpdateData(FALSE);
Метод OnOnTop
Метод OnOnTop во многом похож на уже описанный нами метод OnDblclkFtpLis, но используется не для входа, а для выхода из каталогов. Если вы вошли в каталог и желаете выйти из него на верхний уровень, вы должны нажать кнопку On top. В ответ на сообщение от этой кнопки вызывается метод OnOnTop. Он блокирует список IDC_FTP_LIST, вызывая метод EnableWindow для объекта m_FtpList, управляющего этим списком:// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);
Затем с помощью метода ReverseFind класса CString мы ищем последнее вхождение символа / в строке sCurentDirectory, содержащей имя текущего каталога сервера, содержимое которого показывается на экране.
Если метод ReverseFind не обнаруживает в строке с путем каталога символов /, он возвращает значение -1. Это означает что мы уже находимся в корневом каталоге сервера:
int iNum = sCurentDirectory.ReverseFind('/');
if(iNum == -1)
{
// Если символ / не обнаружен, значит мы находимся в
// корневом каталоге
AfxMessageBox("No top directory");
}
Если же символ / найден, то обращаясь к методу Left мы удаляем все символы, расположенные справа от него (включительно). Таким образом, в строке sCurentDirectory теперь будет записан путь каталога верхнего уровня.
Так как мы изменили текущий каталог, содержимое которого показывается на экране, вызываем метод DirectoryView. Он просмотрит имена каталогов и файлов, расположенных по новому пути, и заполнит список:
else
{
// Удаляем из строки с именем текущего каталога названия
// последнего каталога
sCurentDirectory = sCurentDirectory.Left(iNum);
// Меняем форму курсора (курсор “ожидание”)
CWaitCursor wait;
// Отображаем содержимое каталога верхнего уровня
DirectoryView();
}
Перед тем как вызывать метод DirectoryView, мы создаем объект wait класса CWaitCursor. При этом автоматически изменится внешний вид курсора приложения. Как только метод DirectoryView закончит свою работу и управление выйдет из блока else, объект wait будет удален, а внешний вид курсора восстановится.
После этого снимаем блокировку со списка IDC_FTP_LIST и отображаем новый текущий путь в поле IDC_STATUS:
// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);
// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
Метод OnProgressChangeExplorer
В ходе загрузки объектов с сервера, навигатор вызывает метод OnProgressChangeExplorer. В качестве параметров этому методу передается текущее состояние загрузки Progress и максимальное ProgressMax:void CLookDlg::OnProgressChangeExplorer(long Progress, long ProgressMax)
{
. . .
}
Мы проверяем значение параметров Progress и ProgressMax:
if(Progress <= 0 | ProgressMax <= 0)
return;
Если они больше нуля, изменяем значение линейного индикатора. При этом мы принимаем, что индикатор может отображать значения до ProgressMax:
m_Progress.SetPos( (int) (Progress * 100) / ProgressMax);
Метод OnSelchangeComboAddress
Метод OnSelchangeComboAddress вызывается когда пользователь выбирает из списка IDC_COMBO_ADDRESS адрес нового сервера. В нем мы сразу вызываем метод UpdateData с параметром TRUE чтобы записать выбранный адрес в строку m_address:UpdateData(TRUE);
Затем нам остается только вызвать метод Navigate для навигатора IDC_EXPLORER, представленного элементом данных m_explorer. Мы указали только первый параметр этого метода, который определяет адрес для просмотра:
m_explorer.Navigate(
m_address, // Адрес URL для просмотра
NULL, NULL, NULL, NULL
);
Метод Navigate соединяется с сервером, загружает с него указанную страницу и отображает ее в окне навигатора диалоговой панели приложения.
Метод OnStatusCallback
Виртуальный метод OnStatusCallback класса CInternetSession вызывается программным интерфейсом WinInet и информирует приложение о состоянии связи.Если вы желаете использовать метод OnStatusCallback, вы должны наследовать от класса CInternetSession новый класс, в котором следует переназначить метод OnStatusCallback. Затем вы должны вызвать метод EnableStatusCallback и разрешить использование метода OnStatusCallback.
Прототип метода OnStatusCallback мы привели ниже:
virtual void
OnStatusCallback(
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
);
Через параметр dwContext методу OnStatusCallback передается идентификатор контекста операции о состоянии которой информируется приложение. Параметр dwInternetStatus содержит код состояния. По нему вы можете определить причину вызова метода. Дополнительная информация передается в буфере, по адресу lpvStatusInformation. Размер этого буфера передается в параметре dwStatusInformationLength. Ниже представлены возможные значение параметра dwInternetStatus:
Метод OnStatusTextChangeExplorer
Метод OnStatusTextChangeExplorer вызывается навигатором, когда он переходит к выполнению новой операции или когда со страницы WWW загружен текст для строки состояния.Текст, для отображения в строке состояния передается через единственный параметр метода - Text:
void CLookDlg::OnStatusTextChangeExplorer(LPCTSTR Text)
{
. . .
}
Мы выводим этот текст в поле редактирования IDC_STATUS_TEXT, вызывая метод SetDlgItemText класса CWnd:
SetDlgItemText(IDC_STATUS_TEXT, Text);
Метод OnTitleChangeExplorer
Метод OnTitleChangeExplorer вызывается навигатором, когда он загружает заголовок страницы WWW. Текст заголовка передается через параметр Text данного метода:void CLookDlg::OnTitleChangeExplorer(LPCTSTR Text)
{
. . .
}
Мы отображаем заголовок в поле редактирования IDC_TITLE_BAR диалоговой панели приложения. Для этого вызываем метод SetDlgItemText класса CWnd и передаем ему идентификатор поля редактирования и текст заголовка:
SetDlgItemText(IDC_TITLE_BAR, Text);
Метод OpenFile
Если методы GetFile и PutFile вас не устраивают, потому что вы не получаете в этом случае достаточный контроль за передачей и приемом файла или если надо выполнить обмен данными между файлами на сервере FTP и оперативной памятью локального компьютера, то вы можете сначала открыть файл при помощи метода OpenFile, а затем считывать из него или записывать в него данные непосредственно из буфера на локальном компьютере при помощи методов Read и Write класса CInternetFile.Метод OpenFile возвращает указатель на объект класса CInternetFile. Впоследствии вы можете использовать методы класса CInternetFile чтобы выполнять чтение из соответствующего файла или запись.
Приведем прототип метода OpenFile:
CInternetFile* OpenFile(
LPCTSTR pstrFileName,
DWORD dwAccess = GENERIC_READ,
DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY,
DWORD dwContext = 1
);
Имя файла, который надо открыть, передается методу OpenFile через параметр pstrFileName.
Режим, в котором открывается файл, задается параметром dwAccess. Если файл открывается для чтения, то в качестве параметра dwAccess используется значение GENERIC_READ, а если для записи - GENERIC_WRITE.
Параметр dwFlags определяет метод передачи файла, а параметр dwContext - идентификатор контекста. Назначение этих параметров полностью соответствует соответствующим параметрам методов GetFile и PutFile.
После того, как вы открыли файл на сервере, вы можете обращаться только к методам Read, Write и Close класса CInternetFile, а также метод FindFile класса CFtpFileFind. До тех пор, пока файл не будет закрыт, вы не сможете вызвать другие методы для данного сеанса связи с сервером FTP. В противном случае они будут завершаться с ошибкой, имеющий код FTP_ETRANSFER_IN_PROGRESS.
Метод OpenRequest
Метод OpenRequest создает запрос для передачи его серверу WWW. После того, как запрос создан, его можно модифицировать при помощи метода AddRequestHeaders класса CHttpFile, а затем передать серверу, вызвав метод SendRequest, входящий в состав класса CHttpFile.Определены два варианта метода OpenRequest. Ниже мы привели для вас их прототипы:
CHttpFile* OpenRequest(
LPCTSTR pstrVerb,
LPCTSTR pstrObjectName,
LPCTSTR pstrReferer = NULL,
DWORD dwContext = 1,
LPCTSTR* pstrAcceptTypes = NULL,
LPCTSTR pstrVersion = NULL,
DWORD dwFlags = INTERNET_FLAG_EXISTING_CONNECT
);
CHttpFile* OpenRequest(
int nVerb,
LPCTSTR pstrObjectName,
LPCTSTR pstrReferer = NULL,
DWORD dwContext = 1,
LPCTSTR* pstrAcceptTypes = NULL,
LPCTSTR pstrVersion = NULL,
DWORD dwFlags = INTERNET_FLAG_EXISTING_CONNECT
);
Представленные варианты метода OpenRequest отличаются только типом первого параметра. Этот параметр определяет команду, которая передается в запросе.
В первом варианте метода OpenRequest параметр pstrVerb должен содержать указатель на строку с командой в текстовом виде. Если использовать в качестве pstrVerb значение NULL, то по умолчанию передается команда "GET". Вы можете использовать команды, предоставленные в следующей таблице:
| Команды | Описание | ||
| POST | Передать запрос на сервер, содержащий дополнительные данные. Это может быть, например, обращение к расширению CGI или ISAPI в котором ему передаются данные из формы, заполненной пользователем | ||
| GET | Запрос на получение объекта с сервера WWW | ||
| HEAD | Запрос на получение информации об объекте с сервера WWW. Сам объект не принимается | ||
| PUT | Записать данный объект на сервер по определенному адресу URL | ||
| LINK | Устанавливает связь между ресурсом, идентифцируемом адресом URL и другими ресурсами | ||
| DELETE | Предлагает серверу удалить ресурс по данному адресу URL | ||
| UNLINK | Удалляет связи ресурса с указанным адресом URL |
¨ Отметим, что обычно используются только команды GET, HEAD и PUT
Во втором варианте метода OpenRequest параметр nVerb имеет целочисленный тип. В этом случае для задания команды вы должны использовать константы, представленные в следующей таблице.
|
Команда |
Константа |
|
POST |
HTTP_VERB_POST |
|
GET |
HTTP_VERB_GET |
|
HEAD |
HTTP_VERB_HEAD |
|
PUT |
HTTP_VERB_PUT |
|
LINK |
HTTP_VERB_LINK |
|
DELETE |
HTTP_VERB_DELETE |
|
UNLINK |
HTTP_VERB_UNLINK |
class CHttpConnection : public CInternetConnection
{
public:
enum {
_HTTP_VERB_MIN = 0,
HTTP_VERB_POST = 0,
HTTP_VERB_GET = 1,
HTTP_VERB_HEAD = 2,
HTTP_VERB_PUT = 3,
HTTP_VERB_LINK = 4,
HTTP_VERB_DELETE = 5,
HTTP_VERB_UNLINK = 6,
_HTTP_VERB_MAX = 6,
};
. . .
Объект, на который нацелена данная команда передается методу OpenRequest через параметр pstrObjectName. Он должен содержать указатель на строку с именем объекта, например именем файла, исполняемого модуля CGI, расширения ISAPI и т. д.
Все остальные параметры метода OpenRequest являются необязательными. Вы можете их не указывать, тогда для них будут использоваться значения, принятые по умолчанию.
Так, через параметр pstrReferer можно передать адрес URL объекта, из которого вы получили адрес запрашиваемого объекта. По умолчанию этот параметр равен NULL и соответствующий адрес не используется.
Параметр dwContext задает идентификатор контекста для данной операции запроса. Более подробно идентификатор контекста рассматривается в описании метода GetFile класса CFtpConnection.
С помощью параметра pstrAcceptTypes ваше приложение может сообщить серверу тип информации, который оно может воспринять. Например, приложение может принимать и обрабатывать только текстовый файлы, файлы изображений, и т. д.
Если вы не укажете параметр pstrAcceptTypes или запишете в него значение NULL, то сервер будет считать, что приложение способно работать только с текстовыми файлами типа "text/*".
Переменная pstrVersion содержит указатель на строку, в которой определяется версия HTTP, используемая вашим приложением. Если вы опустите этот параметр, то будет использоваться версия HTTP/1.0.
В качестве параметра dwFlags может использоваться один из флагов, определенных в следующей таблице. Они позволяют управлять загрузкой файлов из сети или из кэша.
|
Флаг |
Описание |
|
INTERNET_FLAG_RELOAD |
Указывает на необходимость загрузки запрашиваемого объекта или информации непосредственно с сервера Internet, даже в том случае если данная информация уже содержится в кэше |
|
INTERNET_FLAG_DONT_CACHE |
Предупреждает запись полученной информации к кэш |
|
INTERNET_FLAG_MAKE_PERSISTENT |
Записывает полученную с сервера информацию в кэш и запрещает ее удаление во время операции очистки кэша |
|
INTERNET_FLAG_SECURE |
Использует семантику защищенных транзакций (secure transaction semantics) |
|
INTERNET_FLAG_NO_AUTO_REDIRECT |
Отменяет автоматическое перенаправление запросов методом SendRequest класса CHttpFile. Используется только в запросах по протоколу HTTP |
Метод OpenURL
Метод OpenURL выполняет различные действия для разных типов URL. Вы можете использовать метод OpenURL для доступа к серверу FTP:CStdioFile* OpenURL(
LPCTSTR pstrURL,
DWORD dwContext = 1,
DWORD dwFlags = 0,
LPCTSTR pstrHeaders = NULL,
DWORD dwHeadersLength = 0
);
throw ( CInternetException );
Первый параметр pstrURL является обязательным. Через него вы должны передать указатель на строку, содержащую адрес URL объекта. Метод OpenURL предназначен для получения доступа к серверам FTP, WWW, системе Gopher и файлам на локальном компьютере. Поэтому в качестве схемы доступа URL могут быть указаны только ftp:, gopher:, http: и file:.
Метод OpenURL возвращает указатель на объект класса CStdioFile, соответствующий заданному адресу URL. Заметим, что этот указатель может указывать на объекты различных классов, для которых класс CStdioFile является базовым. Класс объекта на который возвращает указатель метод OpenURL, зависит от типа URL.
Метод OpenURL выполняет разбор полученной им строки URL. Для этого используется уже описанный нами метод AfxParseURL (раздел “Глобальная функция AfxParseURL”).
Если метод OpenURL определяет, что в качестве параметра pstrURL вы указали URL файла на локальном компьютере, то он создает новый объект класса CStdioFile и возвращает указатель на него.
При создании объект класса CStdioFile указываются атрибуты CFile::modeRead и CFile::shareCompat. Поэтому файл открывается только для чтения в режиме совместимости. Любой другой процесс может открыть этот файл несколько раз. Операция вызывает ошибку, если файл уже открыт другим процессом в любом другом режиме кроме режима совместимости.
Если вы указали методу OpenURL строку URL со схемой доступа ftp:, gopher: или http:, то он устанавливает связь с соответствующим объектом, используя для этого функцию InternetOpenUrl из программного интерфейса WinInet.
Установив соединение с объектом URL, метод OpenURL создает новый объект класса CGopherFile, CInternetFile или CHttpFile, в зависимости от типа URL и возвращает указатель на этот объект:
|
Тип URL |
Указатель на объект класса |
|
file: |
CStdioFile |
|
http: |
CHttpFile |
|
gopher: |
CGopherFile |
|
ftp: |
CInternetFile |
CFile -> CStdioFile -> CInternetFile -> CHttpFile
|
-> CGopherFile
Если вы указали URL, не соответствующий схемам доступа ftp:, gopher:, http: и file:, тогда метод OpenURL вернет значение NULL. В режиме отладки также будет выдано сообщение "Error: Unidentified service type:".
Через параметр dwContext методу OpenURL можно передать идентификатор контекста. Этот идентификатор будет использован при вызове функции обратного вызова.
Параметр dwFlags может содержать набор констант (флагов), объединенных оператором ИЛИ. В следующей таблице мы привели краткие описания этих констант:
|
Константа |
Описание |
|
INTERNET_FLAG_RELOAD |
Загрузить данные из сети, даже если они уже получены и записаны в кэше |
|
INTERNET_FLAG_DONT_CACHE |
Не выполнять кэширование принимаемых данных |
|
INTERNET_FLAG_SECURE |
Используется при передаче запросов HTTP с использованием Secure Sockets Layer или PCT |
|
INTERNET_OPEN_FLAG_USE_EXISTING_CONNECT |
Указывает использовать установленное соединение с сервером для новых запросов, передаваемых методом OpenUrl. В противном случае каждый последующий вызов OpenUrl будет создавать новвый сеанс связи |
|
INTERNET_FLAG_PASSIVE |
Используется при работе с серверами FTP. Выполняется пассивное соединение с сервером. Более подробное описание смотрите в документации к Microsoft Visual C++ |
По умолчанию дополнительный заголовок не указывается. Указатель на строку, содержащую дополнительный заголовок запроса HTTP, передается через параметр pstrHeaders.
Длина дополнительного заголовка передается методу OpenURL через параметр dwHeadersLength. Если вы передаете заголовок в строке, заканчивающейся двоичным нулем, то достаточно указать в качестве параметра dwHeadersLength значение -1L.
Метод OpenURL предназначен для загрузки объектов из сети. Он не выполняет операции, специфические для протоколов FTP, HTTP, Gopher. Для этих целей вы должны использовать другие классы (чтобы открыть соединение определенного вида).
Метод PutFile
По сравнению с методом GetFile, метод PutFile выполняет обратную задачу. Он позволяет записать файл с локального компьютера на сервер FTP. Также как и метод GetFile, этот метод не позволяет контролировать или вмешиваться в процесс загрузки.Если вы желаете иметь более полный контроль за загрузкой файла на сервер, вы должны использовать вместо метода PutFile два метода - метод OpenFile класса CFtpConnection и метод Write класса CInternetFile.
Метод OpenFile
открывает и создает файл на сервере FTP, а метод Write позволяет записывать в него порции данных из буфера на локальном компьютере. Приведем прототип метода PutFile:
BOOL PutFile(
LPCTSTR pstrLocalFile,
LPCTSTR pstrRemoteFile,
DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY,
DWORD dwContext = 1
);
Параметр pstrLocalFile указывает имя файла, записанного на локальном компьютере, который передается на сервер FTP и записывается в файл, определенный параметром pstrRemoteFile.
Параметр dwFlags определяет метод передачи файла, а параметр dwContext - идентификатор контекста. Назначение этих параметров полностью соответствует соответствующим параметрам метода GetFile.
Метод QueryInfo
Метод QueryInfo позволяет узнать ответ сервера на переданный ему запрос и заголовки самого запроса. Вы можете использовать данный метод после передачи запроса серверу с помощью метода SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.В состав класса входят три варианта метода QueryInfo, отличающихся набором параметров. Вы можете использовать тот метод, который наиболее подходит для выполнения ваших задач:
BOOL QueryInfo(
DWORD dwInfoLevel,
LPVOID lpvBuffer,
LPDWORD lpdwBufferLength,
LPDWORD lpdwIndex = NULL
) const;
BOOL QueryInfo(
DWORD dwInfoLevel,
CString& str,
LPDWORD dwIndex = NULL
) const;
BOOL QueryInfo(
DWORD dwInfoLevel,
SYSTEMTIME* pSysTime,
LPDWORD dwIndex = NULL
) const;
Тип информации, запрашиваемой методом QueryInfo, определяется параметром dwInfoLevel, одинаковым у всех трех реализаций метода. В качестве этого параметра следует указать комбинацию атрибута и флага, определяющего тип получаемой информации:
| Флаг | Назначение | ||
| HTTP_QUERY_CUSTOM | Определяет имя заголовка и записывает его в буфер lpvBuffer | ||
| HTTP_QUERY_FLAG_REQUEST_HEADERS | Данный флаг позволяет узнать заголовок запроса который был передан на сервер | ||
| HTTP_QUERY_FLAG_SYSTEMTIME | Некоторые заголовки содержат дату и время. Чтобы не утруждать себя разбором строки с датой и временем следует указать данный флаг и воспользоваться третьим вариантом метода QueryInfo, который сразу занесет эти данные в структуру типа SYSTEMTIME. | ||
| HTTP_QUERY_FLAG_NUMBER | Для тех заголовков, которые имеют числовые значения вы можете воспользоваться флагом HTTP_QUERY_FLAG_NUMBER. В этом случае запрашиваемые данные будут получены в виде 32-битового числа. |
В следующей таблице перечислены возможные атрибуты, которые можно указывать в параметре dwInfoLevel:
| Значения параметра lpdwIndex и dwIndex | |
| HTTP_QUERY_MIME_VERSION | |
| HTTP_QUERY_CONTENT_TYPE | |
| HTTP_QUERY_CONTENT_TRANSFER_ENCODING | |
| HTTP_QUERY_CONTENT_ID | |
| HTTP_QUERY_CONTENT_DESCRIPTION | |
| HTTP_QUERY_CONTENT_LENGTH | |
| HTTP_QUERY_ALLOWED_METHODS | |
| HTTP_QUERY_PUBLIC_METHODS | |
| HTTP_QUERY_DATE | |
| HTTP_QUERY_EXPIRES | |
| HTTP_QUERY_LAST_MODIFIED | |
| HTTP_QUERY_MESSAGE_ID | |
| HTTP_QUERY_URI | |
| HTTP_QUERY_DERIVED_FROM | |
| HTTP_QUERY_LANGUAGE | |
| HTTP_QUERY_COST | |
| HTTP_QUERY_WWW_LINK | |
| HTTP_QUERY_PRAGMA | |
| HTTP_QUERY_VERSION | |
| HTTP_QUERY_STATUS_CODE | |
| HTTP_QUERY_STATUS_TEXT | |
| HTTP_QUERY_RAW_HEADERS | |
| HTTP_QUERY_RAW_HEADERS_CRLF |
Данные, полученные методом QueryInfo, записываются либо в буфер lpvBuffer, либо в строку str, либо в структуру типа SYSTEMTIME, в зависимости от того, какой из прототипов метода используется.
Если вы используете первый прототип метода, то размер буфера вы должны записать в переменной типа DWORD и передать указатель на нее через параметр lpdwBufferLength. После вызова метода в эту переменную будет записана длина строки минус единица.
Чтобы перебрать множественные заголовки с одним и тем же именем вы должны использовать индекс заголовка, который передается через параметр lpdwIndex (или dwIndex) и начинается с нулевого значения. Перед вызовом метода запишите в этот параметр индекс заголовка, который надо получить. После того как метод завершит свою работу, в него будет записан индекс следующего заголовка или значение ERROR_HTTP_HEADER_NOT_FOUND.
В случае успешного завершения метод QueryInfo возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод QueryInfoStatusCode
Метод QueryInfoStatusCode позволяет определить код завершения данного запроса HTTP. Вы можете использовать данный метод после передачи запроса серверу с помощью метода SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL:BOOL QueryInfoStatusCode( DWORD& dwStatusCode ) const;
В случае успешного завершения метод QueryInfoStatusCode возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Фактически метод QueryInfoStatusCode является частным случаем метода QueryInfo, который получает информацию об атрибуте HTTP_QUERY_STATUS_CODE.
Код завершения заносится методом QueryInfoStatusCode в переменную, ссылка на которую передается через параметр dwStatusCode. В следующей таблице представлены группы кодов завершения:
| Коды завершения | Описание | ||
| 200-299 | Успешное завершение | ||
| 300-399 | Информация | ||
| 400-499 | Ошибка запроса | ||
| 500-599 | Ошибка сервера |
Некоторые коды завершения, которые будут встречаться вами наиболее часто представлены в следующей таблице:
| Коды завершения | Описание | ||
| 200 | Объект с данным адресом URL обнаружен | ||
| 400 | Неправильный запрос | ||
| 404 | Объект с данным адресом URL не обнаружен | ||
| 405 | Сервер не поддерживает запрашиваемый метод | ||
| 500 | Неизвестная ошибка сервера | ||
| 503 | Сервер перегружен |
Метод QueryOption
Метод QueryOption, позволяет определить различные характеристики сеанса связи. В состав класса CInternetSession входят два метода QueryOption, которые имеют различные параметры. Ниже мы привели прототипы этих методов:BOOL
QueryOption(
DWORD dwOption,
LPVOID lpBuffer,
LPDWORD lpdwBufLen
) const;
BOOL
QueryOption(
DWORD dwOption,
DWORD& dwValue
) const;
В обоих прототипах метода QueryOption первый параметр dwOption типа DWORD определяет информацию, которую надо получить. Через параметр lpBuffer вы должны передать методу QueryOption указатель на буфер, а через параметр lpdwBufLen - размер этого буфера. Метод QueryOption помещает запрашиваемую информацию в буфер lpBuffer, а в lpdwBufLen записывает ее длину.
Если вы используете второй прототип метода QueryOption, то вместо буфера lpBuffer указываться переменная типа DWORD, адрес которой передается через параметр dwValue.
Некоторые наиболее интересные константы, используемые в качестве параметра dwOption, описаны ниже.
Метод ReadString
Метод ReadString считывает строку данных с сервера, ограниченную символом перевода на новую строку. Определены два варианта метода ReadString. Они различаются только способом записи полученной строки.Первый вариант метода ReadString наиболее универсален. Он записывает полученные с сервера данные в строку класса CString, ссылка на которую передается через параметр rString:
virtual BOOL ReadString( CString& rString );
throw ( CInternetException );
Когда все данные получены, данный вариант метода ReadString возвращает значение FALSE.
Второй вариант метода ReadString записывает полученные данные в буфер pstr:
virtual LPTSTR ReadString( LPTSTR pstr, UINT nMax );
throw ( CInternetException );
Максимальный размер буфера вы должны указать в параметре nMax. В конце полученных данных записывается нулевой символ.
Когда все данные получены, метод ReadString, прототип которого приведен выше, возвращает значение NULL.
В случае возникновения ошибок во время приема данных, метод ReadString может вызвать исключение CInternetException. Причины, вызвавшие исключение можно определить, воспользовавшись методами класса CInternetException.
Метод SendRequest
Метод SendRequest позволяет передать подготовленный запрос на сервер WWW.Определены два варианта метода SendRequest, различающиеся типом и количеством параметров. Все параметры метода являются необязательными и их можно не указывать:
BOOL SendRequest(
LPCTSTR pstrHeaders = NULL,
DWORD dwHeadersLen = 0,
LPVOID lpOptional = NULL,
DWORD dwOptionalLen = 0
);
throw ( CInternetException )
BOOL SendRequest(
CString& strHeaders,
LPVOID lpOptional = NULL,
DWORD dwOptionalLen = 0
);
throw ( CInternetException )
Необязательные параметры pstrHeaders и dwHeadersLen или strHeaders, для второго варианта метода, можно использовать для передачи дополнительных заголовков запроса.
Вы можете записать дополнительные заголовки в строку strHeaders или передать указатель на них через параметр pstrHeaders. Если вы не используете строки и работаете с указателями, то вы также должны задействовать параметр dwHeadersLen записав в него размер заголовка.
Если вам требуется передать сразу после заголовка запроса какие-либо данные, вы можете передать методу указатель на них через параметр lpOptional. В этом случае вы также должны записать в параметр dwOptionalLen длину этих данных.
Метод SendRequest возвращает ненулевое значение в случае успешного завершения или ноль в случае ошибки. Данный метод может вызывать исключение CInternetException. Вы можете определить причину исключения, воспользовавшись методами класса CInternetException.
Метод SetOption
Метод SetOption позволяет установить различные характеристики сеанса связи. Прототип метода SetOption практически полностью соответствует прототипу метода QueryOption:BOOL
SetOption(
DWORD dwOption,
LPVOID lpBuffer,
DWORD dwBufferLength
);
BOOL
SetOption(
DWORD dwOption,
DWORD dwValue
);
Метод WriteString
Антипод метода ReadString - метод WriteString записывает указанную ему строку данных в файл на сервере. Указатель на эту строку вы должны передать методу WriteString через параметр pstr:virtual void WriteString( LPCTSTR pstr );
throw CInternetException( );
Если во время передачи данных возникнут какие-либо проблемы, метод WriteString может вызвать исключение CInternetException.
Методы OnPaint и OnQueryDragIcon
Методы OnPaint и OnQueryDragIcon используются приложением FtpView в случае его минимизации чтобы отображать на экране пиктограмму. Эти методы мы оставляем без изменения и рассматривать их не будем. Вы можете узнать о методах OnPaint и OnQueryDragIcon более подробно из 24 тома серии “Библиотека системного программиста”.Методы OnPaint и OnQueryDragIcon создаются во время создания проекта с помощью MFC AppWizard и используются для отображении пиктограммы приложения в случае его минимизации. Данные методы одинаковы для всех приложений, созданных MFC AppWizard и имеющих пользовательский интерфейс на основе диалоговой панели. Поэтому сейчас мы не станем рассматривать их более подробно. Вы можете найти описание этих методов в первой книге, посвященной Microsoft Visual C++ и библиотеке MFC из серии “Библиотека системного программиста” (том 24).
Методы SetWriteBufferSize и SetReadBufferSize
Программный интерфейс WinInet не выполняет буферизации при обмене данными с серверами Internet. Однако если вы используете классы MFC, то можете выполнять буферизацию данных если работаете с методами класса CInternetFile и методами классов, наследуемых от него.Размер буфера для операций чтения и записи устанавливается отдельно. Для этого в состав класса CInternetFile включены методы SetWriteBufferSize и SetReadBufferSize, которые, соответственно, устанавливают размер буфера, для записи и для чтения данных с сервера:
BOOL SetWriteBufferSize( UINT nWriteSize );
BOOL SetReadBufferSize( UINT nReadSize );
Параметры nWriteSize и nReadSize устанавливают размер буфера в байтах.
В случае успешного завершения методы SetWriteBufferSize и SetReadBufferSize возвращают ненулевое значение. Если методы завершились с ошибкой, то они возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Наш сервер WWW
В начале 1997 года мы открыли в сети Internet собственный сервер WWW. На нем вы можете получить подробную информацию о наших изданных книгах, а также о книгах, которые мы планируем издать в ближайшее время.На сервере вы можете бесплатно получить архивы исходных текстов программ из книг серии “Библиотека системного программиста” и прочитать статьи, опубликованные нами в некоторых периодических изданиях. В дальнейшем мы планируем разместить на сервере ответы на вопросы читателей, отзывы читателей и другую полезную информацию.
Адреса нашего WWW-сервера:
: http://www.glasnet.ru/~frolov
http://www.dials.ccas.ru/frolov
Настройка сеанса связи
Как только создан объект класса CInternetSession, представляющий сеанс связи, вы можете воспользоваться методами этого класса, чтобы изменить некоторые его характеристики. Так, например, вы можете установить ограничение на выполнение некоторых операций, которые могут отнять много времени. Для этого вы можете использовать методы SetOption и QueryOption. Заметим, однако, что в большинстве случаев изменять характеристики сеанса связи нет никакой необходимости и вы можете сразу приступить к выполнению основной задачи - соединиться с сервером WWW или FTP, просмотреть структуру его каталогов, прочитать или записать файл.Недостатки приложения FtpView
У приложения FtpView, рассмотренном в предыдущем разделе, есть как минимум один большой недостаток. В то время когда идет загрузка файла с сервера FTP, ход этого процесса никак не отображается и приложение создает впечатление зависшей программы. Только те, кто использует внешний модем, могут догадаться о работе приложения по интенсивному или не очень интенсивному миганию индикаторов на его лицевой панели. Если компьютер оборудован внутренним модемом, вы в принципе также можете следить за его состоянием, если разместите в панели управления Windows 95 (или Windows NT версии 4.0) индикатор работы модема.Этот недостаток нашего приложения обусловлен устройством метода GetFile класса CFtpConnection, так как он не предусматривает возможности извещения приложения о загрузке части файла. Поэтому вместо использования метода GetFile мы предлагаем воспользоваться методом OpenFile класса CFtpConnection и методом Read класса CInternetFile. Метод OpenFile открывает файл, расположенный на сервере, а метод Read позволяет считывать открытый файл по частям.
Кроме того, метод GetFile сразу записывает файл, полученный с сервера, в файл на локальном диске компьютера. Метод Read записывает полученный фрагмент файла с сервера во временный буфер. Это позволяет, например, обеспечить дополнительную обработку данных по мере их получения.
Доработаем приложение FtpView так, чтобы оно показывало ход загрузки файлов с сервера FTP. Для этого сначала загрузите в редактор ресурсов диалоговую панель IDD_FTPVIEW_DIALOG. Немного увеличьте вертикальный размер панели и добавьте внизу панели линейный индикатор progress bar, выбрав для него идентификатор IDC_PROGRESS, и текстовую строку Progress (рис. 2.13). Фрагмент файла FtpView.rc, содержащий шаблон диалоговой панели мы привели в листинге 4.4.
Листинг 2.13. Фрагмент файла FtpView.rc
//////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_FTPVIEW_DIALOG DIALOGEX 0, 0, 352, 215
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "FtpView"
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON "Connect",IDC_CONNECT,196,5,45,15
PUSHBUTTON "On top",IDC_ON_TOP,253,5,40,15
DEFPUSHBUTTON "OK",IDOK,305,5,40,15
CONTROL "List1",IDC_FTP_LIST,"SysListView32",LVS_REPORT |
LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER |
WS_TABSTOP,5,25,340,133
EDITTEXT IDC_FTP_ADDRESS,5,5,183,14,ES_AUTOHSCROLL
LTEXT "Directory:",IDC_STATIC,7,171,33,8
EDITTEXT IDC_STATUS,44,169,301,15,ES_AUTOHSCROLL |
ES_READONLY
CONTROL "Progress1",IDC_PROGRESS, "msctls_progress32",
WS_BORDER, 43,193,302,14
LTEXT "Progress:",IDC_STATIC,7,196,30,8
END
Запустите MFC ClassWizard, перейдите на страницу Member Variables, выберите класс CFtpViewDlg и добавьте к нему управляющий объект для линейного индикатора. Для этого выберите из списка Control IDs идентификатор IDC_PROGRESS и нажмите кнопку Add Variable. Вам будет предложено привязать к линейному индикатору объект класса CProgressCtrl. Введите имя этого объекта. В нашем случае мы использовали имя m_LoadProgress. Нажмите кнопку OK и закройте MFC ClassWizard.
В классе CFtpViewDlg появится новый элемент m_LoadProgress. Он будет добавлен в блоке AFX_DATA, которым управляет MFC ClassWizard:
class CFtpViewDlg : public CDialog
{
// Construction
public:
~CFtpViewDlg();
CFtpViewDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CFtpViewDlg)
enum { IDD = IDD_FTPVIEW_DIALOG };
CProgressCtrl m_LoadProgress;
. . .
//}}AFX_DATA
. . .
}
Кроме того, вносятся изменения в метод DoDataExchange класса CFtpViewDlg. В блоке AFX_DATA_MAP, в котором MFC ClassWizard размещает методы для обмена данными, добавляется новая строка. Она устанавливает связь между линейным индикатором IDC_PROGRESS и объектом m_LoadProgress:
DDX_Control(pDX, IDC_PROGRESS, m_LoadProgress);
Перед тем как использовать линейный индикатор, следует выполнить его настройку - установить границы значений в которых он может меняться и шаг приращения. В принципе, вы можете оставить эти значения по умолчанию. В этом случае линейный индикатор будет отображать значения в интервале от 0 до 100 с шагом 10. Однако мы все же выполним инициализацию линейного индикатора, чтобы программный код приложения был более нагляден.
Загрузите в текстовый редактор Microsoft Visual C++ метод OnInitDialog класса CFtpViewDlg и добавьте к нему вызовы методов SetRange и SetStep, как это показано на листинге 2.14. Обратите внимание, что эти методы вызываются для объекта m_LoadProgress, представляющего линейный индикатор IDC_PROGRESS.
Листинг 2.14. Фрагмент файла FtpViewDlg.cpp, метод OnInitDialog класса CFtpViewDlg
BOOL CFtpViewDlg::OnInitDialog()
{
// Вызываем метод OnInitDialog базового класса CDialog
CDialog::OnInitDialog();
// Устанавливаем пиктограммы, которые будут отображаться
// в случае минимизации диалоговой панели
SetIcon(m_hIcon,TRUE); // Пиктограмма стандартного размера
SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера
//=========================================================
// Устанавливаем границы для линейного индикатора
m_LoadProgress.SetRange(0, 100);
// Устанавливаем шаг приращения для линейного индикатора
m_LoadProgress.SetStep(10);
//=========================================================
// Выполняем инициализацию списка IDC_FTP_LIST
//=========================================================
. . .
}
Наибольшие изменения надо внести в метод FtpFileDownload класса CFtpViewDlg. Мы должны изменить способ загрузки файла с сервера - отказаться от использования метода GetFile и принимать файл по частям, используя методы OpenFile и Read. Так как в этом случае принимаемые данные не записываются в файл, а помещаются во временный буфер, необходимо организовать запись данных из буфера в файл на локальном диске. Для этого мы будем использовать возможности класса CFile. И наконец, по мере загрузки файла с сервера мы будем изменять состояние линейного индикатора IDC_PROGRESS.
Модифицированный метод FtpFileDownload класса CFtpViewDlg, выполняющий все описанные выше действия представлен в листинге 2.15.
Листинг 2.15. Фрагмент файла FtpViewDlg.cpp, метод FtpFileDownload класса CFtpViewDlg
BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
BOOL fResult;
CString sLocalFileName;
// Определяем объект класса CFileDialog, представляющий
// стандартную диалоговую панель Save As, в которой
// по умолчанию выбрано имя файла sFileName
CFileDialog mFileOpen(FALSE, NULL, sFileName);
// Отображаем диалоговую панель Open и позволяем
// пользователю выбрать с помощью нее один файл
int result = mFileOpen.DoModal();
// Проверяем как была закрыта диалоговая панель Open -
// по нажатию кнопки OK или Cancel
if(result == IDCANCEL)
{
// Если пользователь отказался от выбора файлов и
// нажал кнопку Cancel отображаем соответствующее
// сообщение и возвращаем значение FALSE
AfxMessageBox("File not selected");
return FALSE;
}
else if(result == IDOK)
{
// Если пользователь нажал кнопку OK, определяем
// имя выбранного файла
sLocalFileName = mFileOpen.GetPathName();
}
//=========================================================
// Формируем полное имя файла для загрузки его с
// сервера FTP
sFileName = sCurentDirectory + "/" + sFileName;
//=========================================================
// Чтобы узнать размер файла, записанного на сервере FTP,
// создаем объект класса CFtpFileFind
CFtpFileFind m_FtpFileFind(m_FtpConnection);
// Выполняем поиск выбранного нами файла
if(fResult =
m_FtpFileFind.FindFile(_T(sFileName)))
{
// Если поиск закончился успешно, получаем его
// характеристики
fResult = m_FtpFileFind.FindNextFile();
// Временная переменная для хранения размера файла
DWORD dwLength = 0;
// Определяем длину файла
dwLength = m_FtpFileFind.GetLength();
// В соответствии с размером файла устанавливаем новые
// границы для линейного индикатора
m_LoadProgress.SetRange(0, (int)(dwLength/READ_BLOCK) );
// Устанавливаем шаг приращения для линейного индикатора
m_LoadProgress.SetStep(1);
}
// Так как мы искали только один файл, заканчиваем поиск
m_FtpFileFind.Close();
// Определяем указатель на файл сервера
CInternetFile* iFile;
// Открываем выбранный нами файл на сервере FTP
iFile = m_FtpConnection -> OpenFile(
sFileName.GetBuffer(MIN_LEN_BUF),
GENERIC_READ,
FTP_TRANSFER_TYPE_BINARY
);
//=========================================================
// Создаем и открываем файл на локальном диске компьютера
CFile fLocalFile(
sLocalFileName,
CFile::modeCreate | CFile::modeWrite
);
//=========================================================
// Временная переменная. В нее заносится количество байт,
// считанное с файла на сервере
UINT nReadCount = 0;
// Создаем буфер для чтения файла с сервера FTP
void* ptrBuffer;
ptrBuffer = malloc( READ_BLOCK );
// Считываем файл с сервера, записываем его в локальный
// файл и изменяем текущее положение линейного индикатора
do
{
// Если во время операции чтения возникнет исключение
// CInternetException, то оно будет обработано
// соответствующим блоком catch
try
{
// Читаем из файла на сервере READ_BLOCK байт
nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );
}
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // Временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
AfxMessageBox(szErr);
else
AfxMessageBox("GetFtpConnection Error");
// Так как возникла ошибка, значит данные не считаны
nReadCount = 0;
// Удаляем исключение
pEx->Delete();
}
// Записываем информацию, считанную из файла на сервере,
// которая находится в буфере в локальный файл
fLocalFile.Write( ptrBuffer, nReadCount );
// Увеличиваем значение на линейном индикаторе
m_LoadProgress.StepIt();
// Продолжаем чтение файла с сервера до тех пор, пока не
// будет достигнут конец файла
} while (nReadCount == READ_BLOCK);
// После окончания загрузки файла сбрасываем
// линейный индикатор
m_LoadProgress.SetPos( 0 );
// Закрываем файл на сервере
iFile -> Close();
// Удаляем объект iFile
delete iFile;
// Закрываем файл на локальном компьютере
fLocalFile.Close();
// Освобождаем буфер ptrBuffer
free( ptrBuffer );
return fResult;
}
Постройте проект и запустите полученное приложение. Введите адрес сервера FTP и нажмите кнопку Connect. Далее выберите из списка файлов на сервере, файл для загрузке и сделайте по нему двойной щелчок левой кнопкой мыши. Как и ранее, вам будет предложено указать имя файла на локальном компьютере, в который будет записан файл с сервера FTP. Затем начнется процесс загрузки файла, ход которого будет отображаться линейным индикатором в нижней части диалоговой панели приложения (рис. 2.16).

Рис. 2.16. Загрузка файла с сервера FTP
Как вы уже могли заметить, основные изменения в исходных текстах приложения FtpView коснулись только метода FtpFileDownload класса CFtpViewDlg, который собственно и осуществляет загрузку файла с сервера FTP. Рассмотрим подробнее как работает этот метод.
Обработка исключения CInternetException
В приложениях, которые представлены в этой книге, используется ряд методов, которые могут вызвать исключение CInternetException. Мы организуем обработку этих исключений с помощью блоков try и catch. В блок try заключаются методы, способные вызвать исключение CInternetException, а ниже помещается блок catch для обработки этого исключения:try
{
// Вызов метода, который может стать причиной исключения
. . .
}
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024];
if (pEx->GetErrorMessage(szErr, 1024))
AfxMessageBox(szErr);
else
AfxMessageBox("GetFtpConnection Error");
// Удаляем исключение
pEx->Delete();
}
Во всех наших примерах обработчики исключения CInternetException выполняют примерно одни и те же действия. Сначала определяется причина, вызвавшая исключение. Для этой цели вызывается метод GetErrorMessage. Этот метод записывает текстовое сообщение об ошибке в буфер, указанный первым параметром. Размер буфера должен быть указан во втором параметре метода. Если текстовое описание ошибки недоступно, тогда метод GetErrorMessage возвращает нулевое значение и мы отображаем на экране свое собственное сообщение об ошибке.
В завершение блока catch мы удаляем уже обработанное исключение, для чего вызываем метод Delete. Описание этого метода вы не обнаружите в справочной системе, что однако не мешает использовать его в своих приложениях. Если вы не удалите исключение, то по завершении приложения отладчик сообщит вам о наличии неосвобожденных областей памяти.
¨ Обратите внимание, что надо использовать именно метод Delete, а не оператор delete, название которого начинается с маленькой буквы.
Обработка ошибок
В случае возникновения ошибок, многие методы классов WinInet вызывают исключение CInternetException. Чтобы правильно обрабатывать эти исключения, вы должны поместить методы, которые могут вызвать исключение, в блок try и определить соответствующий блок catch, выполняющий его обработку.Общие принципы устройства приложений WinInet
Приложения, взаимодействующие с серверами Internet посредством программного интерфейса WinInet и надстроенных над ним классов библиотеки MFC, должны придерживаться стандартных сценариев установления связи и обмена данными, которые мы рассмотрим в данном разделе.Оператор HINTERNET
Как мы упоминали, конструктор класса CInternetSession вызывает функцию InternetOpen из программного интерфейса WinInet чтобы выполнить инициализацию библиотеки WinInet и создать новый сеанс связи с Internet. Функция InternetOpen возвращает идентификатор типа HINTERNET, который используется при последующих вызовах функций WinInet.Когда вы используете библиотеку MFC, вы можете определить идентификатор сеанса, воспользовавшись оператором HINTERNET, который входит в состав класса CInternetSession:
operator HINTERNET() const;
Орган управления Microsoft Web Browser
После того, как вы вставите орган управления Microsoft Web Browser в проект, он станет доступен в панели инструментов разработки диалоговых панелей. Выберите в окне Project Workspace страницу ResourceView (рис. 4.6). Как видите, приложение имеет три типа ресурсов - диалоговую панель IDD_LOOK_DIALOG, пиктограмму IDR_MAINFRAME и ресурс с описанием версии приложения VS_VERSION_INFO. Это стандартный набор ресурсов приложения с диалоговым интерфейсом, созданного при помощи MFC AppWizard.
Рис. 4.6. Ресурсы приложения Look
Загрузите в редактор ресурсов диалоговую панель IDD_LOOK_DIALOG приложения Look. Если панель инструментов Controls не появилась на экране, откройте ее. Для этого выберите из меню View главного окна Microsoft Visual C++ строку Toolbars. На экране появится диалоговая панель Toolbars (рис. 4.7). Найдите в списке Toolbars переключатель Controls и включите его. Затем нажмите кнопку Close.
Если в списке Toolbars вы не смогли обнаружить переключатель Controls, проверьте, загружена ли диалоговая панель в окно редактирования. Панели управления Microsoft Visual C++ являются контекстно-зависимыми. При работе с различными объектами доступны разные панели управления.

Рис. 4.7. Диалоговая панель Toolbars
Измените диалоговую панель приложения Look в соответствии с листингом 4.х. На рисунке 4.8 мы представили вид диалоговой панели IDD_LOOK_DIALOG в редакторе ресурсов.

Рис. 4.8. Диалоговая панель Look
В верхней части диалоговой панели IDD_LOOK_DIALOG поместите поле редактирования IDC_TITLE_BAR. В нем будет отображаться заголовок просматриваемой страницы WWW. Слева от поля редактирования поместите текстовую строку Title.
Непосредственно под полем IDC_TITLE_BAR разместите список IDC_COMBO_ADDRESS для выбора адреса URL. В списке Address введите адреса URL серверов WWW, которые можно посетить нашим навигатором. Мы использовали следующие адреса серверов:
http://www.glasnet.ru/~frolov
http://www.dials.ccas.ru/frolov
http://www.microsoft.com/
http://www.dials.ccas.ru/
Первые два адреса из представленного списка, указывают на сервер WWW авторов этой книги. Третий адрес принадлежит компании Microsoft, а последний, четвертый адрес, - фирме АО “ДиалогНаука”.
Вы можете ввести в этом списке адреса других, известных вам серверов, которые вы желаете посетить с помощью навигатора Look. Конечно, жестко “зашивать” адреса серверов WWW в приложении плохо, но нашей задачей является не создание законченного приложения, а изучение органа управления Microsoft Web Browser.
В дальнейшем вы можете расширить приложение Look и хранить адреса серверов в базе данных, организовав к ней доступ через драйвер ODBC или интерфейс DAO. Пример работы с базами данных через драйвера ODBC с помощью классов MFC мы приводили в 28 томе серии “Библиотека системного программиста”.
Слева от списка IDC_COMBO_ADDRESS расположите текстовую строку Address. А справа разместите четыре кнопки Navigate, Refresh, Stop и Exit. Присвойте им соответственно идентификаторы IDC_NAVIGATE, IDC_BUTTON_REFRESH, IDC_BUTTON_STOP и IDOK.
Обратите внимание на органы управления, представленные в панели инструментов Controls. В ней появилась новая пиктограмма
(рис. 4.9), которая и представляет Microsoft Web Browser Control. Если в диалоговой панели Toolbars установлен переключатель Show ToolTips, который включает режим кратких подсказок, и вы поместите на указатель мыши на пиктограмму
, то через некоторое время появится название этого органа управления - Microsoft Web Browser.
Рис. 4.9. Панель инструментов Controls
Таким образом, вы можете разместить окно Microsoft Web Browser Control в обычной диалоговой панели точно также, как вы размещаете на ней текстовую строку, кнопку или переключатель. Поместите орган управления Microsoft Web Browser Control в центре диалоговой панели IDD_LOOK_DIALOG и присвойте ему идентификатор IDC_EXPLORER.
Как вы увидите позже, когда навигатор “Microsoft Web Browser Control” не отображает никакой информации, он имеет серый цвет и полностью сливается с самой диалоговой панелью. Чтобы выделить окно навигатора, создайте вокруг него группу с текстом World Wide Web. Эта группа будет отображаться как тонкая рамка с текстом в левом верхнем углу.
Непосредственно под навигатором IDC_EXPLORER разместите поле редактирования IDC_STATUS_TEXT, дополнительно обозначив его текстовой строкой Status и линейный индикатор IDC_PROGRESS с текстовой строкой Progress.
Поля редактирования IDC_TITLE_BAR и IDC_STATUS_TEXT будут использоваться только для отображения текста, поэтому вы можете указать для них в панели свойств атрибут Read-Only.
Файл ресурсов приложения Look представлен в листинге 4.5. В нем вы найдете шаблон диалоговой панели IDD_LOOK_DIALOG, команды для включения в ресурсы пиктограмм, информационный ресурс VS_VERSION_INFO и другие вспомогательные команды.
Листинг 4.5. Файл Look.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Русские ресурсы
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) ||
defined(AFX_TARG_ENU)\r\n"
"#ifdef _WIN32\r\n"
"LANGUAGE 9, 1\r\n"
"#pragma code_page(1252)\r\n"
"#endif\r\n"
"#include ""res\\Look.rc2\r\n"
"#include ""afxres.rc\r\n"
"#endif\0"
END
#endif // APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Пиктограмма приложения
//
IDR_MAINFRAME ICON DISCARDABLE "res\\Look.ico"
//////////////////////////////////////////////////////////////
//
// Диалоговая панель IDD_LOOK_DIALOG
//
IDD_LOOK_DIALOG DIALOGEX 0, 0, 444, 253
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
EXSTYLE WS_EX_APPWINDOW
CAPTION "Look"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_EXPLORER,
"{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}",
WS_TABSTOP,10,44,425,180
COMBOBOX IDC_COMBO_ADDRESS,
47,20,185,50,CBS_DROPDOWN | CBS_SORT |
WS_VSCROLL | WS_GROUP | WS_TABSTOP
PUSHBUTTON "Refresh",IDC_BUTTON_REFRESH,285,20,40,15
PUSHBUTTON "Stop",IDC_BUTTON_STOP,330,20,40,15
GROUPBOX "World Wide Web",IDC_STATIC,5,34,435,195
LTEXT "Address:",IDC_STATIC,8,22,28,8
CONTROL "Progress1",IDC_PROGRESS,
"msctls_progress32",WS_BORDER,
327,236,112,12
EDITTEXT IDC_STATUS_TEXT,47,236,224,12,
ES_AUTOHSCROLL | ES_READONLY
EDITTEXT IDC_TITLE_BAR,47,3,392,13,
ES_AUTOHSCROLL | ES_READONLY
LTEXT "Title:",IDC_STATIC,8,6,16,8
LTEXT "Status:",IDC_STATIC,5,238,23,8
LTEXT "Progress:",IDC_STATIC,293,238,30,8
PUSHBUTTON "Navigate",IDC_NAVIGATE,240,20,40,15
PUSHBUTTON "Exit",IDOK,393,21,44,14
END
#ifndef _MAC
//////////////////////////////////////////////////////////////
//
// Информация о приложении
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "LOOK MFC Application\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "LOOK\0"
VALUE "LegalCopyright", "Copyright © 1997\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "LOOK.EXE\0"
VALUE "ProductName", "LOOK Application\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // !_MAC
//////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_LOOK_DIALOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 437
TOPMARGIN, 7
BOTTOMMARGIN, 246
END
END
#endif // APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Данные для иинициализации диалоговой панели IDD_LOOK_DIALOG
//
IDD_LOOK_DIALOG DLGINIT
BEGIN
IDC_EXPLORER, 0x376, 128, 0
0x0000, 0x0000, 0x002c, 0x0000, 0x027e, 0x0000, 0x0125,
0x0000, 0x0001, 0x0000, 0x8201, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0x002c,
0x0000, 0x0000, 0x0000, 0x004c, 0x0000, 0x1401, 0x0002,
0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x4600, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6248,
0x004b,
IDC_COMBO_ADDRESS, 0x403, 30, 0
0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x672e, 0x616c,
0x6e73, 0x7465, 0x722e, 0x2f75, 0x667e, 0x6f72, 0x6f6c,
0x0076,
IDC_COMBO_ADDRESS, 0x403, 32, 0
0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x642e, 0x6169,
0x736c, 0x632e, 0x6163, 0x2e73, 0x7572, 0x662f, 0x6f72,
0x6f6c, 0x0076,
IDC_COMBO_ADDRESS, 0x403, 26, 0
0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x642e, 0x6169,
0x736c, 0x632e, 0x6163, 0x2e73, 0x7572, 0x002f,
IDC_COMBO_ADDRESS, 0x403, 26, 0
0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x6d2e, 0x6369,
0x6f72, 0x6f73, 0x7466, 0x632e, 0x6d6f, 0x002f,
IDC_COMBO_ADDRESS, 0x403, 20, 0
0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x6d2e, 0x7063,
0x632e, 0x6d6f, 0x002f, 0
END
#endif
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE 9, 1
#pragma code_page(1252)
#endif
#include "res\Look.rc2"
#include "afxres.rc"
#endif
///////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Идентификаторы ресурсов приложения Look определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Мы привели исходный текст файла resource.h в листинге 4.6.
Листинг 4.6. Файл resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by Look.rc
//
#define IDD_LOOK_DIALOG 102
#define IDR_MAINFRAME 128
#define IDC_EXPLORER 1000
#define IDC_BUTTON_BACK 1002
#define IDC_BUTTON_NEXT 1003
#define IDC_PROGRESS 1003
#define IDC_BUTTON_REFRESH 1004
#define IDC_BUTTON_STOP 1005
#define IDC_STATUS_TEXT 1006
#define IDC_TITLE_BAR 1007
#define IDC_NAVIGATE 1008
#define IDC_COMBO_ADDRESS 1012
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 131
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1010
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Приложение ConsoleFtp
Создайте новый проект. Выберите из меню File строку New. На экране появится диалоговая панель New. Выберите из списка, расположенного в этой панели строку Project Workspace и нажмите кнопку OK. Откроется диалоговая панель New Project Workspace. В качестве типа создаваемого приложения укажите Console Application и введите в поле Name имя проекта - ConsoleFtp. Затем нажмите кнопку Create. Microsoft Visual C++ создаст новый проект.Мы будем использовать для нашего приложения классы библиотеки MFC. Чтобы настроить проект соответствующим образом, вы должны выбрать из меню Build строку Settings. На экране откроется диалоговая панель Project Settings, содержащая различные настройки проекта. Откройте на этой диалоговой панели страницу General, выберите из списка Microsoft Foundations Classes строку Use MFC in a Shared DLL и нажмите кнопку OK.
По умолчанию, новый проект не содержит ни одного файла. Создайте новый текстовый файл. Для этого еще раз выберите из меню File строку New и в открывшейся панели New укажите тип текстового файла - Text File. Нажмите кнопку OK. Откроется окно текстового редактора. Наберите в нем текст приложения, представленный нами в листинге 2.1. Сохраните набранный текст в файле под именем ConsoleFtp.cpp, записав его в каталог проекта.
Затем выберите из меню Insert строку Files into Project. На экране появится диалоговая панель Insert Files into Project. Выберите из списка файлов в этой панели файл ConsoleFtp.cpp и нажмите кнопку Add. Файл ConsoleFtp.cpp будет добавлен к проекту.
Листинг 2.1. Файл ConsoleFtp.cpp
//============================================================
// Приложение ConsoleFtp. Выполняет соединение с заданным
// сервером FTP
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
//============================================================
// Включаемый файл для библиотеки MFC
#include
// Включаемый файл для классов WinInet
#include
// Включаемый файл для консольного ввода/вывода
#include
// Включаемый файл для стандартных функций
#include
//============================================================
// Главная функция приложения
//============================================================
int main(int argc, char* argv[])
{
// Если приложение запущено без параметра, отображаем на
// экране формат вызова приложения
if (argc != 2)
{
cout << "Programm format: ConsoleFtp
cout << "
return -1;
}
// Записываем параметр, указанный при вызове приложения в
// текстовую строку sUrlAdress. Он должен содержать URL
// адрес сервера FTP
CString sUrlAdress;
sUrlAdress = argv[1];
// Отображаем адрес сервера FTP на экране
cout << "URL address: " << sUrlAdress << endl << endl;
// Определяем переменные, в которые будут записаны
// отдельные компоненты адреса, указанного пользователем
// Имя сервера
CString sServer;
// Имя объекта на который указывает адрес URL
CString sObject;
// Номер порта
INTERNET_PORT nPort;
// Тип сервиса или тип протокола
DWORD dwServiceType;
// Разбираем адрес URL, записанный в строке sUrlAdress
if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,
sObject, nPort))
{
// В случае ошибки выводим сообщение и завершаем
// работу приложения
cout << "AfxParseURL Error" << endl;
return -1;
}
// Проверяем, соответствует ли указанный адрес URL
// серверу FTP. Для этого тип сервиса должен быть ftp://
if(dwServiceType != AFX_INET_SERVICE_FTP)
{
// Если адрес не соответствует серверу FTP выводим
// соответствующее предупреждение и завершаем приложение
cout << " URL Address not FTP server" << endl;
return -1;
}
// Указатель на объект класса CInternetSession
CInternetSession* m_InternetSession = NULL;
// Инициализируем сеанс работы с WinInet - создаем объект
// класса CInternetSession catch
m_InternetSession = new CInternetSession("Connecter");
// Исключения, вызванные в этом блоке try обрабатываются
// следующим блоком catch
try
{
// Определяем указатель на объект класса CFtpConnection
CFtpConnection* m_FtpConnection = NULL;
// Пытаемся соединииться с сервером sServer
m_FtpConnection =
m_InternetSession -> GetFtpConnection( sServer );
// Выводим сообщение об успешном соединении
cout << "Connect to FTP server established" << endl;
// Закрываем соединение с сервером FTP
m_FtpConnection -> Close();
// Удаляем объект m_FtpConnection
delete( m_FtpConnection );
}
// Так как исключение в блоке try может быть вызвано только
// методом GetFtpConnection, достаточно определить
// обработчик исключения класса CInternetException
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
cout << "Error: " << szErr << endl << endl;
// Удаляем исключение
pEx->Delete();
}
// Завершаем сеанс связи
m_InternetSession -> Close();
// Удаляем объект m_InternetSession
delete(m_InternetSession);
return 0;
}
Постройте проект. Вы получите исполнимый файл приложения ConsoleFtp.exe. Запустите его на выполнение из командной строки MS-DOS Prompt (закрывать Windows не надо). В качестве параметра укажите URL адрес сервера FTP с которым вы желаете соединиться. Чтобы приложение правильно восприняло этот адрес, на забудьте указать в нем тип сервиса ftp.
Например, в ответ на следующую команду, приложение ConsoleFtp будет пытаться выполнить соединение с сервером FTP корпорации Microsoft:
ConsoleFtp.exe ftp://ftp.microsoft.com
Если указанный сервер FTP доступен, приложение выведет на экран сообщение об успешном соединении и завершит свою работу. Ниже мы привели сообщение приложения ConsoleFtp о соединении с сервером ftp.microsoft.com:
URL address: ftp://ftp.microsoft.com
Connect to FTP server established
В некоторых случаях процесс соединения с сервером может занять значительное время, поэтому не торопитесь насильно прервать исполнение нашего приложения. Если сервер не будет обнаружен или произойдет какая-либо другая ошибка, приложение выведет на экран соответствующее сообщение:
URL address: ftp://failer
Error: The server name or address could not be resolved
В данном случае мы попытались установить соединение с сервером FTP по адресу ftp://failer, но такой сервер не был обнаружен.
Если вы желаете запустить приложение ConsoleFtp непосредственно из среды Microsoft Visual C++ или под отладкой, вы должны указать параметры для него в диалоговой панели Project Settings. Чтобы открыть эту панель выберите из меню Build строку Settings. Выберите в панели Project Settings страницу Debug (рис. 2.1), а затем из списка Category строку General.

Рис. 2.1. Диалоговая панель Project Settings, страница Debug
Теперь вы можете ввести аргументы для приложения в строке Program arguments. Когда вы запустите приложение из среды Microsoft Visual C++, ему будут переданы именно эти параметры.
Приложение ConsoleHTTP
В разделе “Приложение Console FTP“ мы привели приложение ConsoleFTP, которое соединяется с заданным сервером FTP. Для этого в нем сначала создается объект класса CInternetSession, представляющий сеанс работы с WinInet, а затем с помощью метода GetFtpConnection предпринимается попытка соединиться с сервером по заданному адресу. Если установить соединение не удается, например, из - за того, что адрес с таким адресом не существует, то метод GetFtpConnection вызывает исключение CInternetException:// Инициализируем сеанс работы с WinInet
CInternetSession* m_InternetSession = NULL;
m_InternetSession = new CInternetSession("Connecter");
try
{
// Пытаемся соединиться с FTP - сервером sServer
CFtpConnection* m_FtpConnection = NULL;
m_FtpConnection =
m_InternetSession -> GetFtpConnection( sServer );
. . .
}
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
. . .
}
Таким образом, о существовании FTP сервера sServer можно судить по тому, вызвал ли метод GetFtpConnection исключение CInternetException. Если исключение не появилось и метод GetFtpConnection вернул не нулевое значение, значит сервер FTP существует и доступен. А если исключение все же было вызвано, значит, скорее всего, сервера с заданным именем просто не существует.
Как вы знаете, класс CInternetSession содержит в себе три метода для соединения с серверами FTP, WWW и Gopher. Это методы GetFtpConnection, GetHttpConnection и GetGopherConnection. Казалось бы достаточно заменить в исходных текстах приложения FtpConnection вызов метода GetFtpConnection на GetHttpConnection и вы сможете использовать это же приложения для поиска серверов WWW.
Проведите небольшой эксперимент и внесите в исходные тексты проекта FtpConnection соответствующие изменения:
// Инициализируем сеанс работы с WinInet
CInternetSession* m_InternetSession = NULL;
m_InternetSession = new CInternetSession("Connecter");
try
{
// Пытаемся соединиться с WWW - сервером sServer
CHttpConnection* m_HttpConnection = NULL;
m_HttpConnection =
m_InternetSession -> GetHttpConnection( sServer );
. . .
}
catch (CInternetException* pEx)
{
// Обрабатываем исключение CInternetException
. . .
}
Постройте проект и запустите полученное приложение. Попытайтесь соединиться с помощью приложения с различными серверами WWW. Очень скоро вы обнаружите что соединение выполняется “успешно” даже с несуществующими серверами.
Чтобы быть уверенным, что сервер WWW по данному адресу не существует или недоступен, вы можете попытаться связаться с ним при помощи любой программы навигатора, например с помощью Microsoft Internet Explorer или Netscape Navigator.
Так, если вы запустите навигатор Microsoft Internet Explorer и попробуете просмотреть с его помощью сервер FTP по адресу http://home.frolov.com/, то скорее всего, получите предупреждающее сообщение, показанное нами на рисунке 3.1. Увы, пока сервера FTP с таким именем не существует.

Рис. 3.1. Сервер http://home.frolov.com/ не найден
Изменим теперь приложение ConsoleFtp так, чтобы оно не только соединялось с сервером WWW, но и запрашивало с него информацию об определенном файле. Создайте новый проект ConsoleHttp таким же образом, как вы создавали проект ConsoleFtp. Укажите что приложение будет использовать библиотеку классов MFC.
Затем создайте новый текстовый файл ConsoleHttp.cpp, наберите в нем программный код, представленный в листинге 3.2 и включите его в проект. Чтобы ускорить набор файла ConsoleHttp.cpp за его основу вы можете взять файл ConsoleFtp.cpp из проекта ConsoleFtp.
Листинг 3.2. Файл ConsoleHttp.cpp
//============================================================
// Приложение ConsoleFtp. Выполняет соединение с заданным
// сервером WWW
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
//============================================================
// Включаемый файл для библиотеки MFC
#include
// Включаемый файл для классов WinInet
#include
// Включаемый файл для консольного ввода/вывода
#include
// Включаемый файл для стандартных функций
#include
//============================================================
// Главная функция приложения
//============================================================
int main(int argc, char* argv[])
{
// Если приложение запущено без параметра, отображаем на
// экране формат вызова приложения
if (argc != 2)
{
cout << "Programm format: ConsoleFtp
cout << "
return -1;
}
// Записываем параметр, указанный при вызове приложения в
// текстовую строку sUrlAdress. Он должен содержать URL
// адрес сервера WWW
CString sUrlAdress;
sUrlAdress = argv[1];
// Отображаем адрес URL на экране
cout << "URL address: " << sUrlAdress << endl << endl;
// Определяем переменные, в которые будут записаны
// отдельные компоненты адреса, указанного пользователем.
// Имя сервера
CString sServer;
// Имя объекта на который указывает адрес URL
CString sObject;
// Номер порта
INTERNET_PORT nPort;
// Тип сервиса или тип протокола
DWORD dwServiceType;
// Разбираем адрес URL, записанный в строке sUrlAdress
if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,
sObject, nPort))
{
// В случае ошибки выводим сообщение и завершаем
// работу приложения
cout << "AfxParseURL Error" << endl;
return -1;
}
// Проверяем, соответствует ли указанный адрес URL
// серверу WWW. Для этого тип сервиса должен быть http://
if(dwServiceType != AFX_INET_SERVICE_HTTP)
{
cout << " URL Address not WWW server" << endl;
return -2;
}
// Указатель на объект класса CInternetSession
CInternetSession* m_InternetSession = NULL;
// Инициализируем сеанс работы с WinInet - создаем объект
// класса CInternetSession
m_InternetSession = new CInternetSession("Connecter");
// Запрос для сервера WWW
CHttpFile* pFile=NULL;
// Определяем указатель на объект класса CFtpConnection
CHttpConnection* m_HttpConnection = NULL;
// Исключения, вызванные в этом блоке try обрабатываются
// следующим блоком catch
try
{
// Пытаемся соединииться с сервером sServer
m_HttpConnection =
m_InternetSession -> GetHttpConnection( sServer );
// Формируем запрос
pFile =
m_HttpConnection -> OpenRequest(
CHttpConnection::HTTP_VERB_HEAD,
sObject
);
// Дополнительный заголовок запроса HTTP
CString headerInfo(_T(
"Accept: text/*\r\nUser-Agent: HTTP Example\r\n"));
// Добавляем к запросу дополнительный заголовок
if(pFile->AddRequestHeaders(headerInfo))
{
// Передаем запрос на сервер
if(pFile->SendRequest())
{
DWORD dwReturnCode;
// Определяем код завершения запроса
if(pFile->QueryInfoStatusCode(dwReturnCode))
{
// Отображаем код завершения запроса на экране
CString sMessage;
sMessage.Format("%d",dwReturnCode);
cout << "SendRequest: " << sMessage <<
endl << endl;
}
else
{
cout << "Request Error" << endl << endl;
}
}
}
}
// Обрабатываем исключение CInternetException
catch (CInternetException* pEx)
{
// Временный буфер для сообщения
TCHAR szErr[1024];
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
cout << "Error: " << szErr <
// Удаляем иссключение
pEx->Delete();
}
// Закрываем запрос
pFile -> Close();
// Удаляем объект pFile из памяти
if(pFile)
delete pFile;
// Закрываем соединение с сервером WWW
m_HttpConnection -> Close();
// Удаляем объект m_FtpConnection
delete( m_HttpConnection );
// Завершаем сеанс связи
m_InternetSession -> Close();
// Удаляем объект m_InternetSession
delete(m_InternetSession);
return 0;
}
Постройте только что созданный проект и запустите приложение, указав ему в качестве параметра адрес WWW страницы. Адрес должен быть полным - он должен определять метод доступа (в данном случае http://), имя сервера на котором страница расположена и полный путь к файлу страницы WWW.
Приложение соединится с сервером, на котором эта страница расположена и запросит о ней информацию. Результаты запроса будут выведены на экран. На рисунке 3.3 мы показали работу приложения ConsoleHttp, запросив с сервера http://dials.ccas.ru/ страницу с именем frolov.htm.

Рис. 3.2. Приложение ConsoleHttp запрашивает страницы http://www.frolov.ru/~frolov и http://www.glasnet.ru/~frolov
Приложение FtpView
Навигатор Microsoft Internet Explorer можно использовать для просмотра серверов FTP и загрузки с них файлов на локальный диск компьютера. Если вы запустите навигатор и введете для просмотра адрес какого-нибудь сервера FTP, например сервера Microsoft, то в окне навигатора будет показан список файлов и каталогов сервера (рис. 2.2).
Рис. 2.2. Microsoft Internet Explorer просматривает корневой каталог сервера FTP по адресу ftp.microsoft.com
В этом разделе мы создадим свое приложение, которое подобно навигатору Microsoft Internet Explorer может просматривать структуру каталогов серверов FTP и даже загружать с них файлы на диск локального компьютера.
С помощью MFC AppWizard создайте проект FtpView, выбрав в качестве интерфейса пользователя диалоговую панель. Все установки оставьте по умолчанию, но отключите переключатель About box, который расположен во второй панели выбора свойств приложения - MFC AppWizard - Step 2 of 4. В результате приложение не будет иметь информационной диалоговой панели About и его исходные тексты будут более просты для понимания.
Приложение, сформированное MFC AppWizard, будет иметь единственную диалоговую панель, которая отображается сразу после ее запуска. О том, как устроено приложение FtpView, вы можете прочитать в 24 томе серии “Библиотека системного программиста” в разделе “Приложение с главной диалоговой панелью”.
Приложение Look
Воспользуйтесь MFC AppWizard и создайте приложение Look, пользовательский интерфейс которого основан на диалоговой панели. Для этого выберите из меню File строку New. На экране появится диалоговая панель New. Выберите из списка в этой панели строку Project Workspace и нажмите на кнопку OK. Теперь на экране откроется диалоговая панель New Project Workspace. В ней вы должны выбрать тип проекта и его название. Чтобы воспользоваться средствами автоматизированной разработки приложений выберите из списка типов проектов Type строку MFC AppWizard (exe). В поле Name введите имя проекта Look. Нажмите кнопку Create.Начнет работать MFC AppWizard. Вам будет предложено заполнить несколько диалоговых панелей MFC AppWizard, в которых определяются свойства создаваемого приложения. В первой такой панели, которая называется MFC AppWizard - Step 1, выберите тип пользовательского интерфейса для приложения. Переключатель What type of application would you like to create переведите в положение Dialog based. Нажмите кнопку Next. На экране появится следующая диалоговая панель MFC AppWizard - Step 2 of 4. В группе What OLE support would you like to include этой панели вы должны включить переключатель OLE controls.
¨ Если вы работаете в Microsoft Visual C++ версии 5.0, вы должны иметь в виду, что в диалоговой панели MFC AppWizard - Step 2 of 4 изменились названия некоторых полей и переключателей. Это связано с тем, что технология OLE стала именоваться как ActiveX. Так группа What OLE support would you like to include стала называться What ActiveX support would you like to include
Все остальные параметры приложения можно оставить по умолчанию и нажать на кнопку Finish. На экране появится панель New Project Information, содержащая список свойств создаваемого приложения. Нажмите кнопку OK и MFC AppWizard создаст для вас проект Look.
Теперь надо добавить к проекту орган управления Microsoft Web Browser Control, с помощью которого приложение Look будет обмениваться данными с Internet.
Выберите из меню Insert строку Component. На экране появится диалоговая панель Component Gallery. Откройте страницу OLE Controls этой панели (рис. 4.2). На странице OLE Controls представлены органы управления OCX, которые вы можете использовать в ваших приложениях.

Рис. 4.2. Диалоговая панель Component Gallery
Среди органов управления, представленных на странице OLE Controls в диалоговой панели Component Gallery, выберите орган управления Microsoft Web Browser Control. Для большинства органов управления, представленных в панели Component Gallery, можно получить их описание. Для этого достаточно нажать кнопку ?. К сожалению, при выборе пиктограммы Microsoft Web Browser Control, кнопка ? остается заблокированной.
Когда вы устанавливаете Internet Explorer, орган управления ActiveX подключается к операционной системе. Microsoft Visual C++ обнаруживает установленные органы ActiveX и позволяет использовать их в своих приложениях. Фактически программный код органа управления ActiveX расположен в файле Shdocvw.dll, который вы можете обнаружить в системном каталоге Windows.
¨ Если вы используете Microsoft Visual C++ более ранней версии, чем 5.0, вы можете получить информацию об органе управления Microsoft Web Browser Control можно на WWW-сервере компании Microsoft, по адресу http://www.microsoft.com в документе Web Browsing Objects for C/C++ Developers. Начиная с версии 5.0 соответствующее описание включено в документацию Microsoft Visual C++.
![]() |
Орган управления Microsoft Web Browser Control предоставляет возможность просмотра WWW страниц. |
Список классов, которые будут созданы для работы с органом управления Microsoft Web Browser Control содержит название только одного класса - CWebBrowser. Обратите внимание на то, что переключатель отображаемый перед названием класса CWebBrowser должен быть включен.
В поле Class name отображается имя выбранного класса. Вы можете изменить его по своему усмотрению. Имя базового класса для класса CWebBrowser отображается в поле Base class. Данное поле выполняет исключительно информационные функции, так как изменить имя базового класса в панели Confirm Classes невозможно. В качестве базового класса для CWebBrowser выступает класс CWnd.
Имена включаемого файла и файла реализации, в которых будут помещены определение класса CWebBrowser и его методов, отображаются в полях Headr file и Implementation file. По умолчанию используются имена файлов WebBrowser.h и WebBrowser.cpp. В случае необходимости вы можете указать для этих файлов другие имена.

Рис. 4.3. Диалоговая панель Confirm Classes
Когда все поля диалоговой панели Confirm Classes заполнены, нажмите кнопку OK. Для нашего приложения мы оставили имя класса органа управления Microsoft Web Browser Control, а также имена включаемого файла и файла реализации без изменения.
После того, как вы добавите в проект орган управления Microsoft Web Browser, просмотрите список входящих в него файлов. Для этого откройте окно Project Workspace и перейдите на страницу FileView (рис. 4.4).

Рис. 4.4. Исходные файлы приложения Look
В проект будет добавлен класс CWebBrowser, представленный файлами WebBrowser.h и WebBrowser.cpp. Полный список классов приложения Look можно посмотреть в окне Project Workspace на странице ClassView (рис. 4.5). Мы не стали раскрывать на рисунке содержимое классов, так как класс CWebBrowser включает в себя слишком много методов.

Рис. 4.5. Классы приложения Look
В состав проекта Look входят всего три класса, коротко описанные в следующей таблице.
|
Класс |
Описание |
|
CLookApp |
Главный класс приложения Look |
|
CLookDlg |
Класс, управляющий диалоговой панелью приложения |
|
CWebBrowser |
Класс органа управления Microsoft Web Browser Control |
Приложение Parse
В этом разделе мы представим вашему вниманию приложение Parse. Оно использует функцию AfxParseURL для разбора на составные части адресов URL, которые пользователь может вводить в диалоговой панели приложения (рис. 1.3).Приложение Parse не выполняет соединение с Internet и вы можете его запустить на компьютере, не имеющем ни модема, ни сетевой карты, и не подключенном к сетям Internet и Intranet. Конечно, пользы от такого приложения не много, но мы предлагаем вам с ним поработать, чтобы лучше разобраться со структурой адресов URL.

Рис. 1.3. Приложение ParseURL
Чтобы упростить приложение, мы выбрали для него интерфейс на основе диалоговой панели. Запустите MFC AppWizard, выбрав из меню File строку New. На экране появится одноименная диалоговая панель. Выберите в ней из списка New строку Project Workspace и нажмите кнопку OK. Откроется диалоговая панель New Project Workspace. В качестве типа создаваемого приложения укажите MFC AppWizard (exe) и введите в поле Name имя проекта - Parse. Затем нажмите кнопку Create.
На экране появится первая диалоговая панель MFC AppWizard - MFC AppWizard - Step 1. В ней вы должны выбрать тип пользовательского интерфейса приложения - Dialog based, то есть приложение с главной диалоговой панелью. В принципе, теперь вы можете нажать кнопку Finish и оставить все остальные характеристики приложения по умолчанию. Но чтобы упростить приложение и сделать его исходный текст более доступным для понимания, мы предлагаем вам сначала перейти к следующей панели MFC AppWizard Step 2 of 4 и отключить в ней переключатель About box. При этом MFC AppWizard не будет подключать к приложению информационную диалоговую панель About. За счет этого можно несколько упростить исходные тексты приложения.
После этого, нажмите кнопку Finish, просмотрите информацию о приложении, которая появится в диалоговой панели New Project Information, и нажмите на кнопку OK. MFC AppWizard создаст проект.
Мы уже детально рассматривали шаблон приложения с пользовательским интерфейсом на основе диалоговой панели в 28 томе серии “Библиотека системного программиста”. Поэтому мы сразу приступим к доработке шаблона приложения и не будем останавливаться на исходных текстах, созданных MFC AppWizard.
Сначала загрузите в редактор ресурсов шаблон главной диалоговой панели IDD_PARSEURL_DIALOG приложения Parse. Разместите на этой диалоговой панели пять полей редактирования IDC_EDIT_URL_ADDRESS, IDC_EDIT_SERVICE_TYPE, IDC_EDIT_SERVER_NAME, IDC_EDIT_OBJECT_NAME и IDC_EDIT_PORT_NUMBER. Все поля редактирования кроме IDC_EDIT_URL_ADDRESS сделайте доступными только для чтения. Для этого в панели свойств этих полей Edit Propeties на странице Styles установите переключатель Read-only. Около каждого поля редактирования сделайте соответствующие текстовые подписи URL Address, Service type, Server name, Object name и Port number.
По умолчанию MFC AppWizard создает в диалоговой панели IDD_PARSEURL_DIALOG две кнопки - кнопку OK с идентификатором IDOK и кнопку Cancel с идентификатором IDCANCEL. Кнопка Cancel нам не понадобится вы можете ее удалить. Вместо нее добавьте кнопку Convert, присвоив ей идентификатор IDC_BUTTON_CONVERT.
В листинге 1.1 мы привели фрагмент файла ресурсов приложения Parse, в котором определяется шаблон диалоговой панели IDD_PARSEURL_DIALOG. Ориентируйтесь на него, когда будете создавать эту диалоговую панель.
Листинг 1.1. Фрагмент файла Parse.rc, шаблон диалоговой панели IDD_PARSEURL_DIALOG
//////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_PARSEURL_DIALOG DIALOGEX 0, 0, 241, 210
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "ParseURL"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,184,183,50,14
EDITTEXT IDC_EDIT_URL_ADDRESS,7,27,227,15, ES_AUTOHSCROLL
LTEXT "URL Address",IDC_STATIC,7,15,43,8
EDITTEXT IDC_EDIT_SERVICE_TYPE,7,73,227,15,ES_AUTOHSCROLL |
ES_READONLY
LTEXT "Service type",IDC_STATIC,7,61,40,8
EDITTEXT IDC_EDIT_SERVER_NAME,7,110,227,15,ES_AUTOHSCROLL |
ES_READONLY
LTEXT "Server name",IDC_STATIC,7,97,41,8
EDITTEXT IDC_EDIT_OBJECT_NAME,7,147,227,15,ES_AUTOHSCROLL |
ES_READONLY
LTEXT "Object name",IDC_STATIC,7,135,41,8
EDITTEXT IDC_EDIT_PORT_NUMBER,7,182,52,15, ES_AUTOHSCROLL |
ES_READONLY
LTEXT "Port number",IDC_STATIC,7,170,39,8
PUSHBUTTON "Convert",IDC_BUTTON_CONVERT,115,183,50,14
END
Все идентификаторы, которые создаются автоматически во время редактирования ресурсов приложения, в том числе диалоговой панели IDD_PARSEURL_DIALOG, записываются в файле resource.h. Исходный текст этого файла мы привели в листинге 1.2.
Листинг 1.2. Файл resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ParseURL.rc
//
#define IDM_ABOUTBOX 0x0010
#define IDD_ABOUTBOX 100
#define IDS_ABOUTBOX 101
#define IDD_PARSEURL_DIALOG 102
#define IDR_MAINFRAME 128
#define IDC_EDIT_URL_ADDRESS 1000
#define IDC_EDIT_SERVICE_TYPE 1001
#define IDC_EDIT_SERVER_NAME 1002
#define IDC_EDIT_OBJECT_NAME 1003
#define IDC_EDIT_PORT_NUMBER 1004
#define IDC_BUTTON_CONVERT 1005
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1006
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
После того, как вы завершите подготовку диалоговой панели IDD_PARSEURL_DIALOG, запустите MFC ClassWizard. С его помощью мы привяжем к полям редактирования диалоговой панели новые элементы данных класса CParseURLDlg.
В диалоговой панели MFC ClassWizard перейдите на страницу Member Variables. Выберите в поле Class name имя класса CParseURLDlg и добавьте к нему с помощью кнопки Add Variable пять переменных, привязав их к полям ввода. К полю IDC_EDIT_URL_ADDRESS привяжите переменную m_UrlAddress, к IDC_EDIT_SERVICE_TYPE - m_ServiceType, IDC_EDIT_SERVER_NAME - m_ServerName и к IDC_EDIT_PORT_NUMBER - m_PortNumber. В качестве типа переменных выберите для всех них класс CString. Тогда используя механизм DDX вы легко сможете обмениваться данными между полями редактирования и соответствующими строками.
В исходных текстах приложения MFC ClassWizard модифицирует конструктор и метод DoDataExchange класса CParseURLDlg. В конструкторе класса CParseURLDlg добавляется программный код, выполняющий инициализацию строк, привязанных к полям редактирования диалоговой панели. Как видно из листинга 1.3, соответствующие команды добавляются внутри блока AFX_DATA_INIT.
Листинг 1.3. Фрагмент файла Parse.cpp, конструктор класса CParseURLDlg
CParseURLDlg::CParseURLDlg(CWnd* pParent /*=NULL*/)
: CDialog(CParseURLDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CParseURLDlg)
m_ObjectName = _T("");
m_PortNumber = _T("");
m_ServerName = _T("");
m_ServiceType = _T("");
m_UrlAddress = _T("");
//}}AFX_DATA_INIT
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
К методу DoDataExchange класса CParseURLDlg MFC ClassWizard добавляет методы DDX_Text, фактически осуществляющие обмен данными между полями редактирования диалоговой панели и соответствующими текстовыми строками. Мы привели фрагмент файла Parse.cpp с определением метода DoDataExchange в листинге 1.4.
Листинг 1.4. Фрагмент файла Parse.cpp, метод DoDataExchange класса CParseURLDlg
void CParseURLDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CParseURLDlg)
DDX_Text(pDX, IDC_EDIT_OBJECT_NAME, m_ObjectName);
DDX_Text(pDX, IDC_EDIT_PORT_NUMBER, m_PortNumber);
DDX_Text(pDX, IDC_EDIT_SERVER_NAME, m_ServerName);
DDX_Text(pDX, IDC_EDIT_SERVICE_TYPE, m_ServiceType);
DDX_Text(pDX, IDC_EDIT_URL_ADDRESS, m_UrlAddress);
//}}AFX_DATA_MAP
}
Мы разместили на диалоговой панели IDD_PARSEURL_DIALOG кнопку Convert. Еще раз воспользуйтесь MFC ClassWizard и добавьте к классу CParseURLDlg обработчик сообщений от этой кнопки. Он будет вызываться когда пользователь будет нажимать на кнопку. MFC ClassWizard предложит вам назвать соответствующий метод именем OnButtonConvert. Согласитесь с этим предложением и в таблице сообщений класса CParseURLDlg появится новая макрокоманда:
BEGIN_MESSAGE_MAP(CParseURLDlg, CDialog)
//{{AFX_MSG_MAP(CParseURLDlg)
. . .
ON_BN_CLICKED(IDC_BUTTON_CONVERT, OnButtonConvert)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Кроме того, в определении класса CParseURLDlg будет добавлено описание метода OnButtonConvert. Мы привели определение класса CParseURLDlg в листинге 1.5.
Листинг 1.5. Фрагмент файла Parse.h, определение класса CParseURLDlg
class CParseURLDlg : public CDialog
{
// Construction
public:
CParseURLDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CParseURLDlg)
enum { IDD = IDD_PARSEURL_DIALOG };
CString m_ObjectName;
CString m_PortNumber;
CString m_ServerName;
CString m_ServiceType;
CString m_UrlAddress;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CParseURLDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CParseURLDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnButtonConvert();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Определение метода OnButtonConvert размещается в файле Parse.cpp вместе с остальными методами класса CParseURLDlg. MFC ClassWizard создает только шаблон метода, который не выполняет никаких действий. Вы должны самостоятельно доработать метод OnButtonConvert, загрузив его в текстовый редактор Microsoft Visual C++. В листинге 1.6 мы привели фрагменты файла Parse.cpp с определением метода OnButtonConvert класса CParseURLDlg.
Листинг 1.6. Фрагмент файла Parse.cpp, метод OnButtonConvert класса CParseURLDlg
void CParseURLDlg::OnButtonConvert()
{
// Получаем адрес, введенный в диалоговой панели
UpdateData(TRUE);
CString sServer; // Имя сервера
CString sObject; // Имя объекта (с каталогами)
INTERNET_PORT nPort; // Номер порта TCP/IP
DWORD dwServiceType;
// Тип сервиса или протокола
// Разбираем адрес URL, записанный в строке m_UrlAddress
if (!AfxParseURL(m_UrlAddress, dwServiceType, m_ServerName,
m_ObjectName, nPort))
{
AfxMessageBox("AfxParseURL Error");
m_ObjectName = "";
m_PortNumber = "";
m_ServerName = "";
m_ServiceType = "";
}
else
{
switch(dwServiceType)
{
case AFX_INET_SERVICE_FTP:
m_ServiceType = "AFX_INET_SERVICE_FTP";
break;
case AFX_INET_SERVICE_HTTP:
m_ServiceType = "AFX_INET_SERVICE_HTTP";
break;
case AFX_INET_SERVICE_GOPHER:
m_ServiceType = "AFX_INET_SERVICE_GOPHER";
break;
case AFX_INET_SERVICE_FILE:
m_ServiceType = "AFX_INET_SERVICE_FILE";
break;
default:
m_ServiceType.Format("%d", dwServiceType);
}
m_PortNumber.Format("%d", nPort);
}
// Выводим полученные значения в диалоговой панелли
UpdateData(FALSE);
}
Для наглядности во время запуска приложения мы выполняем инициализацию поля ввода адреса URL, записывая в него строку http://host:80/bin/programm/main.htm. Этот адрес содержит все возможные элементы - тип протокола http, имя сервера host, номер порта 80, а также путь и имя объекта /bin/programm/main.htm, на который и указывает данный адрес URL.
Соответствующий программный код, инициализирующий строку адреса надо добавить к методу OnInitDialog класса CParseURLDlg. Мы привели исходный текст этого метода в листинге 1.7.
Листинг 1.7. Фрагмент файла Parse.cpp, метод OnInitDialog класса CParseURLDlg
BOOL CParseURLDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// . . .
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Инициализируем поле адреса URL
m_UrlAddress = "http://host:80/bin/programm/main.htm";
// Отображаем строку m_UrlAddress в диалоговой панели
UpdateData(FALSE);
return TRUE;
}
Теперь в исходный текст приложения внесены все необходимые изменения. Вы можете построить проект и запустить приложение. На экране появится диалоговая панель ParseURL, внешний вид которой мы уже приводили ранее на рисунке 4.4.
Изначально в поле URL Address отображается адрес http://host:80/bin/programm/main.htm, который мы взяли в качестве примера. Вы можете изменить его по своему усмотрению - поменять тип протокола обмена, имя сервера, путь и имя объекта и номер порта. Вы даже можете попытаться ввести неправильный адрес, не соответствующий формату адресов URL.
Нажмите кнопку Convert. Приложение разберет введенный вами адрес URL на составные части и выведет их в других полях диалоговой панели. Так в поле Service type будет показан тип сервиса (протокола обмена). Это может быть либо текстовая строка, либо число. В поле Server name вы увидите имя сервера, а в поле Object name - имя объекта, включая путь. Если в адрес URL включен номер порта, то он будет показан в поле Port number.
Если вы введете в поле адреса URL неправильную строку, то на экране появится сообщение об ошибке, а поля Service type, Server name, Object name и Port number будут очищены.
Когда вы закончите экспериментировать с разбором различных адресов URL завершите приложение. Для этого достаточно нажать кнопку OK.
Приложение WebHelp
Создайте новый проект под названием WebHelp. В качестве типа приложения выберите из списка Type строку Application. Наберите в редакторе исходный текст приложения и сохраните его в файле WebHelp.cpp (листинг 5.1). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFMenu, которое мы приводили в 24 томе.Включите файл WebHelp.cpp в проект. Теперь надо перейти к созданию ресурсов, используемых в приложении.
Листинг 5.1. Файл WebHelp.cpp
//============================================================
// Приложение WebHelp: справочная система на основе WWW
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
//============================================================
// Включаемый файл для MFC
#include
#include "WebHelpRes.h"
//=====================================================
// Класс CWebHelpApp - главный класс приложения
//=====================================================
class CWebHelpApp : public CWinApp
{
public:
// Мы будем переопределять метод InitInstance,
// предназначенный для инициализации приложения
virtual BOOL InitInstance();
};
// Создаем объект приложение класса CWebHelpApp
CWebHelpApp WebHelpApp;
//=====================================================
// Класс CWebHelpWindow - представляет главное окно
//=====================================================
class CWebHelpWindow : public CFrameWnd
{
public:
// Объявляем конструктор класса CWebHelpWindow
CWebHelpWindow();
// Объявляем методы для обработки команд меню
afx_msg void WebHelpCommand();
afx_msg void AboutCommand();
afx_msg void ExitAppCommand();
// Макрокоманда необходима, так как класс
// CWebHelpWindow обрабатывает сообщения
DECLARE_MESSAGE_MAP()
};
//=====================================================
// Метод MenuCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CWebHelpWindow::WebHelpCommand()
{
// Запускаем навигатор для просмотра справочной
// информации, записанной на сервере WWW
ShellExecute(NULL, "open",
"http://www.dials.ccas.ru/frolov/rwin/webhelp.htm",
NULL, NULL, SW_SHOWNORMAL);
}
//=====================================================
// Метод MenuCommand
// Обрабатывает команду ID_TEST_BEEP
//=====================================================
void CWebHelpWindow::AboutCommand()
{
MessageBox("World Wide Web Help, \n(c) Frolov Grigory");
}
//=====================================================
// Метод ExitApp
//=====================================================
void CWebHelpWindow::ExitAppCommand()
{
DestroyWindow();
}
//=====================================================
// Таблица сообщений класса CWebHelpWindow
//=====================================================
BEGIN_MESSAGE_MAP(CWebHelpWindow, CFrameWnd)
ON_COMMAND(ID_HELP_WEBHELP, WebHelpCommand)
ON_COMMAND(ID_HELP_ABOUT, AboutCommand)
ON_COMMAND(ID_FILE_EXIT, ExitAppCommand)
END_MESSAGE_MAP()
//=====================================================
// Метод InitInstance класса CWebHelpApp
//=====================================================
BOOL CWebHelpApp::InitInstance()
{
// Создаем объект класса CWebHelpWindow
m_pMainWnd = new CWebHelpWindow();
// Отображаем окно на экране
m_pMainWnd -> ShowWindow(m_nCmdShow);
// Обновляем содержимое окна
m_pMainWnd -> UpdateWindow();
return TRUE;
}
//=====================================================
// Конструктор класса CWebHelpWindow
//=====================================================
CWebHelpWindow::CWebHelpWindow()
{
// Создаем окно приложения, соответствующее
// данному объекту класса CWebHelpWindow
Create(NULL, "World Wide Web Help", WS_OVERLAPPEDWINDOW,
rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));
}
Процедура загрузки файла с сервера FTP
Чтобы добавить к классу CFtpViewDlg метод FtpFileDownload, воспользуйтесь средствами MFC ClassWizard. В определении класса CFtpViewDlg появится объявление нового метода (листинг 2.10). Соответствующий фрагмент кода мы привели в листинге 3.3. Определение метода FtpFileDownload будет добавлено в файле FtpViewDlg.cpp. Исправьте этот метод так, как это показано в листинге 2.11.Листинг 2.10. Файл FtpViewDlg.h, фрагмент определения класса CFtpViewDlg
//////////////////////////////////////////////////////////////
// Класс CFtpViewDlg
class CFtpViewDlg : public CDialog
{
. . .
DECLARE_MESSAGE_MAP()
private:
BOOL FtpFileDownload( CString sFileName );
};
Метод FtpFileDownload запрашивает у пользователя имя и расположение файла на локальном диске компьютера, в который будет загружен файл с сервера, выбранный из списка в главной диалоговой панели приложения (листинг 2.11). Когда имя файла определено, метод FtpFileDownload выполняет загрузку файла, считывая его непосредственно с сервера FTP.
Листинг 2.11. Файл FtpViewDlg.cpp, метод FtpFileDownload
//============================================================
// Метод FtpFileDownload класса CFtpViewDlg
// Загружает файл с сервера FTP
//============================================================
BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
BOOL fResult;
CString sLocalFileName;
// Определяем объект класса CFileDialog, представляющий
// стандартную диалоговую панель Save As, в которой
// по умолчанию выбрано имя файла sFileName
CFileDialog mFileOpen(FALSE, NULL, sFileName);
// Отображаем диалоговую панель Open и позволяем
// пользователю выбрать с помощью нее один файл
int result = mFileOpen.DoModal();
// Проверяем как была закрыта диалоговая панель Open -
// по нажатию кнопки OK или Cancel
if(result == IDCANCEL)
{
// Если пользователь отказался от выбора файла и
// нажал кнопку Cancel отображаем соответствующее
// сообщение и возвращаем значение FALSE
AfxMessageBox("File not selected");
return FALSE;
}
else if(result == IDOK)
{
// Если пользователь нажал кнопку OK, определяем
// имя выбранного файла
sLocalFileName = mFileOpen.GetPathName();
}
sFileName = sCurentDirectory + "/" + sFileName;
fResult =
m_FtpConnection ->
GetFile(sFileName.GetBuffer(MIN_LEN_BUF),
sLocalFileName.GetBuffer(MIN_LEN_BUF),
FALSE
);
return fResult;
}
Остается только исправить метод OnDblclkFtpList так, чтобы когда пользователь выберет файл из списка в главной диалоговой панели приложения, то выполнялся вызов метода FtpFileDownload класса CFtpViewDlg. На листинге 2.12 мы привели фрагмент файла FtpViewDlg.cpp, который содержит новый вариант метода OnDblclkFtpList.
Листинг 2.12. Файл FtpViewDlg.cpp, метод OnDblclkFtpList
//============================================================
// Метод OnDblclkFtpList класса CFtpViewDlg
// Переходит в выбранный каталог
//============================================================
void CFtpViewDlg::OnDblclkFtpList(NMHDR* pNMHDR,
LRESULT* pResult)
{
int iTotalNumber; // Количество элементов списка
CString sSelItem; // Название каталога
CString sLength_Dir; // Длина файла или строка Dir
// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);
// Определяем количество элементов в списке IDC_FTP_LIST
iTotalNumber = m_FtpList.GetItemCount();
// Определяем, какой объект списка выбран
for(int i = 0; i < iTotalNumber; i++)
{
// Атрибут LVIS_SELECTED установлен
// у выбранного элемента списка
if(LVIS_SELECTED == m_FtpList.GetItemState( i,
LVIS_SELECTED ))
{
// Определяем название выбранного элемента списка
// (имя файла или каталога)
sSelItem = m_FtpList.GetItemText( i, 0 );
// Считываем данные из колонки Length
sLength_Dir = m_FtpList.GetItemText( i, 1 );
if(sLength_Dir == "Dir") // Выбран каталог
{
// Переходим в выбранный каталог
sCurentDirectory =
sCurentDirectory + "/" + sSelItem;
// Меняем форму курсора
CWaitCursor wait;
// Отображаем содержимое выбранного каталога
DirectoryView();
// Отображаем новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
}
else // Выбран файл
{
// Меняем форму курсора
CWaitCursor wait;
FtpFileDownload(sSelItem);
}
break;
}
}
// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);
*pResult = 0;
}
Заново постройте проект и запустите приложение. На экране появится главная диалоговая панель, с помощью которой вы можете соединиться с сервером FTP и просмотреть содержимое его каталогов.
Однако теперь, когда вы делаете двойной щелчок левой клавишей мыши по имени файла, вместо предупреждающего сообщения на экране появляется стандартная диалоговая панель Save As с предложением сохранить выбранный файл на локальном диске компьютера.
На рисунке 2.14 мы показали предложение приложения FtpView сохранить файл с именем readme.txt, расположенный на сервере FTP корпорации Microsoft (адрес ftp.microsoft.com) в каталоге developer, в файле readme.txt на локальном диске компьютера в каталоге Library.

Рис. 2.14. Выбор имени файла на локальном компьютере
По умолчанию в панели Save As вам предлагается имя файла, соответствующее имени исходного файла на сервере FTP. Вы можете оставить его без изменения или поменять по своему усмотрению. Если вы выберите на локальном компьютере уже существующий файл, когда приложение запросит подтверждение на его замену (рис. 2.15).

Рис. 2.15. Файл уже существует
Рассмотрим как работают методы OnDblclkFtpList и FtpFileDownload.
Работа приложения FtpView
Запустите приложение FtpView. На экране появится главная диалоговая панель приложения (рис. 2.11). В поле редактирования, расположенном в верхнем левом углу окна отображается адрес сервера FTP. В качестве примера мы привели адрес сервера АО “ДиалогНаука”. Вы можете изменить этот адрес по вашему усмотрению и ввести адрес известного вам сервера FTP.Заметим, что вы должны указать только имя сервера без префикса ftp://, имен подкаталогов и имен файлов. Приложение FtpView не выполняет каких-либо проверок введенной строки и передает ее непосредственно методу FtpConnect класса CInternetSession.
Нажмите кнопку Connect. Приложение FtpView попытается установить связь с указанным сервером FTP. Если такая попытка закончится успешно, приложение выведет в диалоговой панели содержимое текущего каталога сервера FTP. Путь текущего каталога вы можете просмотреть в поле Directory. В зависимости от настройки сервера это может быть корневой каталог или один из его подкаталогов.
Обратите внимание, что соединяясь с сервером, вы не указываете имя пользователя и пароль. Поэтому по умолчанию предполагается, что вы входите на сервер под именем anonimys и с паролем, соответствующим имени приложения.
Если сервер FTP, с которым вы соединяетесь, не позволяет с ним работать незарегистрированным пользователям, то на экране появится соответствующее сообщение об ошибке. Вы можете доработать приложение FtpView так, чтобы оно позволяло задавать имя пользователя и пароль. Для этого достаточно передать их методу GetFtpConnection через параметры pstrUserName и pstrPassword.

Рис. 2.11. Приложение FtpView
Список содержимого текущего каталога сервера отображается в таблице, состоящей из четырех столбцов - Name, Length, Date и Time. В столбце Name отображаются имена каталогов и файлов, расположенных в данном каталоге. Остальные три столбца - Length, Date и Time включают информацию о размере, дате и времени создания или изменения соответствующего каталога или файла. Каталоги выделяются пиктограммой
, расположенной перед его именем и строкой Dir в столбце Length, а файлы только пиктограммой
.Чтобы войти в каталог, надо сделать двойной щелчок мышью по его названию. Приложение попытается просмотреть содержимое указанного вами каталога и выведет его в таблице диалоговой панели приложения. При этом содержимое старого каталога будет скрыто. Чтобы вернуться в каталог верхнего уровня, надо нажать кнопку On top. Таким образом вы можете просматривать структуру каталогов сервера FTP.
Если выберать из списка не каталог, а файл, то на экране появится предупреждающее сообщение и вы останетесь в текущем каталоге. На рисунке 2.12 показано сообщения, которое появляется при выборе файла. В данном случае мы выбрали файл с именем README.DOC.

Рис. 2.12. Выбран файл README.DOC
Работа с файлами
Другая группа методов класса CFtpConnection предназначена для работы с файлами:| Метод | Описание | ||
| Rename | Переименовывает файл, находящийся на сервере FTP | ||
| Remove | Удаляет файл с сервера FTP | ||
| PutFile | Записывает файл на сервер | ||
| GetFile | Позволяет загрузить определенный файл с сервера FTP | ||
| OpenFile | Открывает файл, расположенный на сервере FTP для последующего чтения или записи |
Еще один метод, который входит в состав класса CFtpConnection - Close. Этот метод закрывает соединение с сервером FTP, для чего он вызывает метод Close класса CInternetConnection. Деструктор класса CFtpConnection закрывает соединение с сервером даже если вы не вызвали метод Close.
Мы рассмотрим только три метода для работы с файлами. Это методы GetFile, PutFile и OpenFile. Описание остальных методов вы найдете в справочной системе Microsoft Visual C++.
Работа со структурой каталогов сервера FTP
Среди методов класса CFtpConnection можно выделить группу методов, ориентированных на работу со структурой каталогов сервера FTP. В следующей таблице мы представили краткие описания этих методов:| Метод | Описание | ||
| SetCurrentDirectory | Позволяет перейти в заданный каталог на сервере FTP | ||
| GetCurrentDirectory | Определяет имя текущего каталога на сервере FTP. Смотри также метод GetCurrentDirectoryAsURL | ||
| GetCurrentDirectoryAsURL | Определяет адрес URL текущего каталога на сервере FTP | ||
| RemoveDirectory | Удаляет определенный каталог с сервера FTP | ||
| CreateDirectory | Создает на сервере FTP новый каталог |
Рассмотрим для примера методы GetCurrentDirectory и GetCurrentDirectoryAsURL. Другие методы, предназначенные для работы со структурой каталогов сервера FTP, вы можете изучить самостоятельно.
Ресурсы приложения FtpView
Загрузите шаблон диалоговой панели IDD_FTPVIEW_DIALOG приложения в редактор ресурсов (рис. 2.3).В верхней левой части панели создайте поле редактирования для ввода адреса сервера FTP, с идентификатором IDC_FTP_ADDRESS. С правой стороны от этого поля расположите кнопку Connect с идентификатором IDC_CONNECT, кнопку On top с идентификатором IDC_ON_TOP и кнопку OK.
¨ По умолчанию MFC AppWizard размещает на диалоговой панели кнопки OK и Cancel. Кнопку OK вы можете использовать как есть, а кнопку Cancel мы предлагаем вам удалить, так как она будет нам не нужна.
В центре диалоговой панели разместите список (орган управления List Control) и присвойте ему идентификатор IDC_FTP_LIST. В этом списке будет отображаться содержимое каталогов сервера FTP.

Рис. 2.3. Редактирование диалоговой панели FtpView
Список List Control может отображать информацию в различных форматах. Это могут быть пиктограммы большого или маленького размера с подписями, отсортированные различным образом, или табличная информация разделенная на несколько колонок. Мы будем использовать последний из перечисленных форматов.
Откройте панель свойств списка List Control Properties. Мы показали ее на рисунке 2.4. Перейдите на страницу Styles. Из списка View выберите строку Report, которая определяет, что список будет отображать табличную информацию. Из списка Align выберите строку Left. Она задает выравнивание строк, отображаемых в списке, по левой границе. Вы также можете определить будут ли сортироваться строки, отображаемые в списке. Мы выбрали прямой порядок сортировки. Вы, в принципе, можете использовать обратный порядок сортировки или отказаться от сортировки совсем.

Рис. 2.4. Свойства списка IDC_FTP_LIST
В нижней части диалоговой панели разместите текстовую надпись Directory и поле редактирования для отображения названия текущего каталога с идентификатором IDI_DIRECTORY. Поле IDI_DIRECTORY будет использоваться только для вывода текста, поэтому откройте диалоговую панель свойств этого органа управления, перейдите на страницу Styles и включите переключатель Read-only (рис. 2.5).

Рис. 2.5. Свойства поля редактирования IDI_DIRECTORY
Кроме диалоговой панели IDD_FTPVIEW_DIALOG в ресурсы приложения FtpView входит пиктограмма IDR_MAINFRAME и ресурс, содержащий информацию о версии приложения VS_VERSION_INFO. Вы можете оставить ресурс VS_VERSION_INFO и пиктограмму IDR_MAINFRAME без изменения, но мы заменили стандартную пиктограмму приложений, созданных MFC AppWizard рисунком, который для нас нарисовал художник Алексей Абрамкин.
Вы должны добавить к ресурсам приложения еще две пиктограммы IDI_FILE и IDI_DIRECTORY, на которых изображены “лист бумаги” и “папка”. Эти пиктограммы будут использоваться для выделения файлов и каталогов в списке объектов каталога сервера FTP. Пиктограммы IDI_FILE и IDI_DIRECTORY обязательно должны иметь “маленькие” изображения размером 8 х 8 пикселов. Стандартные изображения 16 х 16 пикселов можно не рисовать - для нашего примера они не потребуются.
В следующей таблице мы привели изображения пиктограмм, которые используются в нашем приложении. В таблице все пиктограммы одинакового размера, но на самом деле пиктограммы IDI_FILE и IDI_DIRECTORY меньше чем пиктограмма IDR_MAINFRAME в два раза.
|
Пиктограмма |
Идентификатор пиктограммы |
Имя файла |
![]() |
IDI_FILE |
file.ico |
![]() |
IDI_DIRECTORY |
director.ico |
![]() |
IDR_MAINFRAME |
russian.ico |
Ресурс VS_VERSION_INFO, который описывает версию приложения, нами не используется и мы оставляем его без изменения таким, каким он создан MFC AppWizard.

Рис. 2.6. Ресурсы приложения
Файл ресурсов приложения FtpView представлен в листинге 2.2. В нем вы найдете шаблон диалоговой панели IDD_FTPVIEW_DIALOG, команды для включения пиктограмм, информационный ресурс VS_VERSION_INFO и другие вспомогательные команды.
Листинг 2.2. Файл FtpView.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Русские ресурсы
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) ||
defined(AFX_TARG_ENU)\r\n"
"#ifdef _WIN32\r\n"
"LANGUAGE 9, 1\r\n"
"#pragma code_page(1252)\r\n"
"#endif\r\n"
"#include ""res\\FtpView.rc2"" \r\n"
"#include ""afxres.rc"" \r\n"
"#endif\0"
END
#endif // APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Диалоговая панель IDD_FTPVIEW_DIALOG
//
IDD_FTPVIEW_DIALOG DIALOGEX 0, 0, 352, 194
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "FtpView"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,305,5,40,15
CONTROL "List1",IDC_FTP_LIST,"SysListView32",
LVS_REPORT | LVS_SORTASCENDING |
LVS_ALIGNLEFT | WS_BORDER |
WS_TABSTOP,5,25,340,133
EDITTEXT IDC_FTP_ADDRESS,5,5,183,14,ES_AUTOHSCROLL
PUSHBUTTON "On top",IDC_ON_TOP,253,5,40,15
PUSHBUTTON "Connect",IDC_CONNECT,196,5,45,15
LTEXT "Directory:",IDC_STATIC,7,171,33,8
EDITTEXT IDC_STATUS,44,169,301,15,ES_AUTOHSCROLL | ES_READONLY
END
#ifndef _MAC
//////////////////////////////////////////////////////////////
//
// Информация о приложении
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "FTPVIEW MFC Application\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "FTPVIEW\0"
VALUE "LegalCopyright", "Copyright © 1997\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "FTPVIEW.EXE\0"
VALUE "ProductName", "FTPVIEW Application\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // !_MAC
//////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_FTPVIEW_DIALOG, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 345
TOPMARGIN, 4
BOTTOMMARGIN, 187
END
END
#endif // APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Пиктограммы приложения
//
IDR_MAINFRAME ICON DISCARDABLE "res\\Russian.ico"
IDI_FILE ICON DISCARDABLE "res\\File.ico"
IDI_DIRECTORY ICON DISCARDABLE "res\\director.ico"
#endif
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE 9, 1
#pragma code_page(1252)
#endif
#include "res\FtpView.rc2"
#include "afxres.rc"
#endif
//////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Идентификаторы ресурсов приложения FtpView определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла resource.h представлен в листинге 2.3.
Листинг 2.3. Файл resource.h
//{{NO_DEPENDENCIES}}
// Файл создан Microsoft Developer Studio
// Используется в FtpView.rc
// Идентификаторы органов управления
#define IDD_FTPVIEW_DIALOG 102
#define IDR_MAINFRAME 128
#define IDI_DIRECTORY 130
#define IDI_FILE 131
#define IDC_FTP_LIST 1000
#define IDC_FTP_ADDRESS 1001
#define IDC_VIEW 1002
#define IDC_ON_TOP 1003
#define IDC_CONNECT 1004
#define IDC_STATUS 1005
// Следующие значения идентификаторов используются по
// умолчанию для новых объектов
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1006
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
Ресурсы приложения
Приложение WebHelp должно содержать меню, из которого можно вызвать для просмотра в навигаторе WWW страницу со справочным текстом. Чтобы включить в проект меню, сначала создайте новый файл ресурсов. Выберите из меню File строку New, а затем из открывшейся диалоговой панели выберите строку Resource Script и нажмите кнопку OK.Теперь выберите из меню Insert строку Resource. На экране появится диалоговая панель Insert Resource. Выберите из нее строку Menu и нажмите кнопку OK. Вы сможете разработать меню в диалоговом режиме. Чтобы быстрее перейти к редактированию меню, можно нажать кнопку New Menu (
) из панели управления Project.Создайте меню Help, при выборе которой открывается меню, содержащее две строки - Web Help и About. В диалоговой панели свойств элементов меню (рис. 5.2) установите переключатель Help, тогда для этого меню будет установлено выравнивание по правому краю.
Затем в меню верхнего уровня добавьте строку File. В меню File поместите строку Exit. Строки Web Help и About из меню Help должны иметь идентификаторы ID_HELP_WEBHELP и ID_HELP_ABOUT, а строка Exit меню File - идентификатор ID_FILE_EXIT.

Рис. 5.2. Диалоговая панель Menu Item Properties
Сохраните файл ресурсов в файле с именем WebHelp.rc. Редактор ресурсов создает кроме самого файла ресурсов еще включаемый файл, в котором определяются константы, используемые в файле ресурсов. В нашем случае в нем определяются идентификаторы меню приложения. По умолчанию этот файл сохраняется под именем resource.h. Вы можете изменить его, выбрав из меню View строку Resource Includes. Для нашего приложения мы изменили имя включаемого файла для ресурсов на WebHelpRes.h. Содержимое этого файла представлено листингом 5.2.
Листинг 5.2. Файл WebHelpRes.h
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by WebHelp.rc
//
#define IDR_MENU 101
#define ID_HELP_WEBHELP 40003
#define ID_FILE_EXIT 40005
#define ID_HELP_ABOUT 40007
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40008
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
В листинге 5.3 мы привели файл ресурсов WebHelpRes.rc приложения. Этот файл был подготовлен редактором ресурсов Visual C++. Одна из первых строк файла содержит директиву #include, которой подключается файл WebHelpRes.h, содержащий описание идентификаторов ресурсов (листинг 5.2).
Среди прочих служебных строк, необходимых редактору ресурсов и компилятору Visual C++, вы можете обнаружить описание меню приложения IDR_MENU.
Листинг 5.3. Файл WebHelpRes.rc
//Microsoft Developer Studio generated resource script.
//
#include "WebHelpRes.h"
#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Russian resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32
//////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU MENU DISCARDABLE
BEGIN
POPUP "File"
BEGIN
MENUITEM "Exit", ID_FILE_EXIT
END
POPUP "Help", HELP
BEGIN
MENUITEM "Web Help", ID_HELP_WEBHELP
MENUITEM SEPARATOR
MENUITEM "About", ID_HELP_ABOUT
END
END
#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"WebHelpRes.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Russian resources
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
//////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Теперь проект готов. Вы можете построить его и запустить полученное приложение WebHelp. Внешний вид приложения представлен на рисунке 5.3. Окно приложения имеет меню File и Help. В меню File расположена только одна строка Exit, выбрав которую вы можете завершить приложение. Меню Help состоит из двух строк - Web Help и About.

Рис. 5.3. Приложение WebHelp
Если вы выберите строку About, то на экране появится временная диалоговая панель с информацией об приложении. Когда же вы выбираете из меню Help строку Web Help, будет запущена программа навигатор, которая используется вами по умолчанию и в нее будет загружена WWW страница, расположенная по адресу http://www.dials.ccas.ru/frolov/rwin/webhelp.htm. Мы специально подготовили эту страницу, расположив ее на нашем сервере и поместили на ней краткие инструкции по работе с программой.
Обратите внимание, что описанная нами программа работает вне зависимости от того, какой навигатор вы используете - Microsoft Internet Explorer, Netscape Navigator или какую-либо другую программу.
Сеанс связи
Любое приложение, использующее классы и методы WinInet, должно, в первую очередь, инициализировать сеанс работы с интерфейсом WinInet или, другими словами, сеанс связи. Для этого следует создать объект класса CInternetSession. Одно приложение может установить несколько сеансов связи, если того требует выполняемая им задача.Только после этого в рамках данного сеанса возможна дальнейшая работа с серверами Internet, будь то установление связи с серверами WWW или FTP, чтение и запись файлов, поиск файлов по заданному адресу и т. д. Единственное что вы можете делать с программным интерфейсом WinInet до того, как создан сеанс - это выполнять разбор адресов URL при помощи глобальной функции AfxParseURL. Описание этой функции и пример ее использования вы найдете в разделе “Адреса URL”.
По окончании работы следует закрыть сеанс связи и удалить соответствующий объект класса CInternetSession, если он был создан динамически.
Таблица сообщений класса CFtpViewDlg
Таблица сообщений класса CFtpViewDlg состоит из макрокоманд BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, определяющие сообщения обрабатываемые данным классом. Как видите, они расположены в блоке AFX_MSG_MAP, поэтому для управления ими используется ClassWizard.Необработанные сообщения передаются базовому классу CDialog, так как он указан во втором параметре макрокоманды BEGIN_MESSAGE_MAP:
//============================================================
// Таблица сообщений класса CFtpViewDlg
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewDlg, CDialog)
//{{AFX_MSG_MAP(CFtpViewDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
// Сообщение от кнопки Connect
ON_BN_CLICKED(IDC_CONNECT, OnConnect)
// Сообщение от кнопки On Top
ON_BN_CLICKED(IDC_ON_TOP, OnOnTop)
// Сообщение с кодом извещения NM_DBLCLK от списка
ON_NOTIFY(NM_DBLCLK, IDC_FTP_LIST, OnDblclkFtpList)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Две первые макрокоманды, расположенные в данной таблице сообщений - ON_WM_PAINT и ON_WM_QUERYDRAGICON. При помощи ClassWizard вы можете обнаружить, что данные макрокоманды выполняют обработку сообщений WM_PAINT и WM_QUERYDRAGICON, вызывая для этого методы OnPaint и OnQueryDragIcon.
Для обработки сообщений от кнопок Connect с идентификатором IDC_CONNECT и On Top с идентификатором IDC_ON_TOP макрокоманды ON_BN_CLICKED вызывают методы OnOnTop и OnConnect, определенные в классе CFtpViewDlg. Таблица сообщений класса CFtpViewDlg не содержит макрокоманд для обработки сообщений от кнопки OK, которая имеет идентификатор IDOK, и поэтому для нее вызывается метод OnOK базового класса CDialog.
Последняя макрокоманда таблицы сообщений - ON_NOTIFY. Она вызывает метод OnDblclkFtpList для обработки сообщений с кодом извещения NM_DBLCLK от списка IDC_FTP_LIST. Сообщение с таким кодом извещения вырабатывается списком, когда пользователь делает в нем двойной щелчок левой клавишей мыши.
Таблицы сообщений класса CLookDlg
Первые две макрокоманды, таблицы сообщений класса CLookDlg, присутствуют во всех приложениях, имеющих пользовательский интерфейс на основе диалоговой панели. Макрокоманды ON_WM_PAINT и ON_WM_QUERYDRAGICON выполняют обработку сообщений WM_PAINT и WM_QUERYDRAGICON, вызывая для этого методы OnSysCommand, OnPaint и OnQueryDragIcon.Следующая макрокоманда ON_CBN_SELCHANGE обрабатывает сообщение от списка IDC_COMBO_ADDRESS, которое передается когда пользователь выбирает из него строку.
Последующие пять макрокоманд ON_BN_CLICKED вызывают обработчики сообщений от кнопок IDC_BUTTON_REFRESH, IDC_BUTTON_STOP, IDC_BUTTON_BACK, IDC_BUTTON_NEXT и IDC_NAVIGATE, когда пользователь нажимает на них:
BEGIN_MESSAGE_MAP(CLookDlg, CDialog)
//{{AFX_MSG_MAP(CLookDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_CBN_SELCHANGE(IDC_COMBO_ADDRESS,
OnSelchangeComboAddress)
ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh)
ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
ON_BN_CLICKED(IDC_BUTTON_BACK, OnButtonBack)
ON_BN_CLICKED(IDC_BUTTON_NEXT, OnButtonNext)
ON_BN_CLICKED(IDC_NAVIGATE, OnNavigate)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Таблица сообщений класса CLookDlg, выделенная в блоке BEGIN_MESSAGE_MAP, не содержит в себе ничего необычного, за исключением того, что в ней отсутствуют обработчики сообщений навигатора. Несмотря на то что мы использовали MFC ClassWizard для переопределения ряда методов навигатора, это не нашло никакого отражения в таблице сообщений.
Однако не спешите думать, что в наше приложение закрались какая-то ошибка. Просмотрите файл LookDlg.cpp до конца. Вы обнаружите еще одну таблицу сообщений, обозначенную макрокомандами BEGIN_EVENTSINK_MAP. Эта таблица также относится к классу CLookDlg, наследованному от базового класса CDialog, как это указано в заголовке таблицы:
BEGIN_EVENTSINK_MAP(CLookDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CLookDlg)
ON_EVENT(CLookDlg, IDC_EXPLORER, 106 /* DownloadBegin */,
OnDownloadBeginExplorer, VTS_NONE)
ON_EVENT(CLookDlg,IDC_EXPLORER, 104 /* DownloadComplete */,
OnDownloadCompleteExplorer, VTS_NONE)
ON_EVENT(CLookDlg, IDC_EXPLORER, 108 /* ProgressChange */,
OnProgressChangeExplorer, VTS_I4 VTS_I4)
ON_EVENT(CLookDlg, IDC_EXPLORER, 100 /* BeforeNavigate */,
OnBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
VTS_PVARIANT VTS_BSTR VTS_PBOOL)
ON_EVENT(CLookDlg,IDC_EXPLORER,200 /*FrameBeforeNavigate*/,
OnFrameBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
VTS_PVARIANT VTS_BSTR VTS_PBOOL)
ON_EVENT(CLookDlg, IDC_EXPLORER, 113 /* TitleChange */,
OnTitleChangeExplorer, VTS_BSTR)
ON_EVENT(CLookDlg, IDC_EXPLORER, 103 /* Quit */,
OnQuitExplorer, VTS_PBOOL)
ON_EVENT(CLookDlg,IDC_EXPLORER, 102 /* StatusTextChange */,
OnStatusTextChangeExplorer, VTS_BSTR)
ON_EVENT(CLookDlg, IDC_EXPLORER, 101 /*NavigateComplete */,
OnNavigateCompleteExplorer, VTS_BSTR)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
Все макрокоманды ON_EVENT в этой таблице сообщений обрабатывают сообщения от навигатора IDC_EXPLORER. Макрокоманда ON_EVENT имеет пять параметров.
Первый параметр определяет имя класса которому принадлежит таблица сообщений. В данном случае все сообщения органа управления обрабатываются классом CLookDlg, которому и принадлежит таблица сообщений AFX_EVENTSINK_MAP.
Второй параметр определяет идентификатор органа управления сообщения от которого обрабатываются. В главной диалоговой панели нашего приложения есть только один орган управления ActiveX, сообщения которого обрабатываются - это навигатор IDC_EXPLORER.
Третий параметр содержит идентификатор метода, который перехватывается данной макрокомандой. Данный идентификатор представляет собой целое число, однозначно определяющее сообщение органа управления. MFC ClassWizard вставляет после этого идентификатора комментарий с именем соответствующего метода.
Имя метода, который определен в нашем приложении и вызывается для обработки данного сообщения, указывается в четвертом параметре макрокоманды. По умолчанию эти имена присваиваются MFC ClassWizard на основе имени сообщения.
И, наконец, последний пятый параметр макрокоманды определяет типы параметров метода обработчика. В качестве этого параметра может фигурировать один или несколько идентификаторов VTS_, разделенных символами пробела.
Описание идентификаторов VTS_ вы можете найти в документации Microsoft Visual C++. Сейчас мы опишем только те идентификаторы, которые фигурируют в таблице сообщений приложения:
|
Идентификатор |
Тип параметра |
|
VTS_I4 |
long |
|
VTS_BSTR |
const char* |
|
VTS_PVARIANT |
VARIANT* |
|
VTS_PBOOL |
BOOL FAR* |
|
VTS_NONE |
Параметры отсутствуют |
Устройство приложения ConsoleFtp
Для приложения ConsoleFtp мы использовали операторы потокового ввода/вывода << и >>, а также функции new и free. Поэтому нам понадобилось включить файлы iostream.h и stdlib.h:#include
#include
Наряду со стандартными функциями библиотеки языка Си, наше приложение будет пользоваться классами WinInet, входящими в библиотеку MFC. Поэтому мы включили в исходный текст приложения файлы afx.h и afxinet.h:
#include
#include
Определяя главную функцию приложения, мы указали для нее параметры argc и argv. Они позволяют получить параметры, указанные при запуске приложения в командной строке:
int main(int argc, char* argv[])
{
}
¨ Напомним, что через argc передается количество параметров указанных приложению, а через argv - список указателей на эти параметры
Пользователь должен указать нашему приложению всего один параметр, содержащий URL адрес сервера FTP. Если приложение запущено без параметра или указано большее количество параметров, то мы отображаем на экране информацию о формате вызова приложения:
if (argc != 2)
{
cout << "Programm format: ConsoleFtp
cout << "
return -1;
}
Параметр, указанный при вызове приложения, записываем в текстовую строку sUrlAdress. Если параметр задан правильно он должен содержать URL адрес сервера FTP:
CString sUrlAdress;
sUrlAdress = argv[1];
Отображаем адрес сервера FTP на экране, чтобы быть уверенным в том что он задан правильно:
cout << "URL address: " << sUrlAdress << endl << endl;
Далее мы объявляем временные переменные, которые будут использоваться при разборе адреса URL. Целью такого разбора будет выделение из адреса URL имени сервера FTP:
// Имя сервера
CString sServer;
// Имя объекта на который указывает адрес URL
CString sObject;
// Номер порта
INTERNET_PORT nPort;
// Тип сервиса или тип протокола
DWORD dwServiceType;
Сам разбор адреса URL, записанного в строке sUrlAdress, выполняется с помощью функции AfxParseURL. Заметим, что это пожалуй единственная функция библиотеки MFC, относящаяся к WinInet, которая не требует предварительно создать сеанс связи - объект класса CInternetSession.
Исходный адрес URL передается функции AfxParseURL через параметр sUrlAdress, а результат записывается в переменные dwServiceType, sServer, sObject, nPort. Нас будут интересовать только две переменные dwServiceType и sServer. В переменную dwServiceType функция AfxParseURL записывает тип сервиса, указанный в адресе sUrlAdress, а в строку sServer - имя сервера:
if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,
sObject, nPort))
{
cout << "AfxParseURL Error" << endl;
return -1;
}
if(dwServiceType != AFX_INET_SERVICE_FTP)
{
cout << "URL Address not FTP server" << endl;
return -1;
}
Если вы указали неправильный адрес URL или тип сервиса, не соответствующий серверу FTP, то на экран выводится предупреждающее сообщение и работа приложения завершается. Если адрес сервера указан верно, приложение создает сеанс связи с Internet. Для этого мы объявляем указатель на объект класса CInternetSession, а затем создаем сам объект, используя функцию new:
// Указатель на объект класса CInternetSession
CInternetSession* m_InternetSession = NULL;
// Инициализируем сеанс работы с WinInet - создаем объект
// класса CInternetSession catch
m_InternetSession = new CInternetSession("Connecter");
Конструктор класса CInternetSession имеет много параметров, но мы все их используем по умолчанию, кроме первого параметра, через который мы задаем имя сеанса - Connecter. Если вы опустите и этот параметр конструктора, то в качестве имени сеанса будет взято имя приложения.
¨ В случае консольного приложения попытка определить его имя по умолчанию приводит к ошибке, которую вы сможете заметить, если запустите приложение в режиме отладки
Теперь можно попытаться соединиться с сервером FTP. Для этого следует вызвать метод GetFtpConnection класса CInternetSession только что созданного объекта m_InternetSession. В случае ошибки этот метод может вызвать исключение CInternetException. Поэтому вызов метода GetFtpConnection мы помещаем в блок try и определяем для него соответствующий блок catch, обрабатывающий исключение CInternetException:
// Исключения, вызванные в этом блоке try обрабатываются
// следующим блоком catch
try
{
// . . .
}
catch (CInternetException* pEx)
{
// . . .
}
Обработчик исключения CInternetException вызывает метод GetErrorMessage для полученного исключения. Он записывает во временный буфер szErr причину вызова исключения. Если текстовое описание исключения доступно, метод GetErrorMessage возвращает ненулевое значение. В этом случае мы отображаем полученный текст на экране:
// Обрабатываем исключение CInternetException
TCHAR szErr[1024]; // временный буфер для сообщения
// Выводим сообщение об ошибке
if (pEx->GetErrorMessage(szErr, 1024))
cout << "Error: " << szErr << endl << endl;
Затем завершаем обработку исключения, вызывая для него метод Delete. Он удаляет объект, представляющий исключение, из оперативной памяти:
// Удаляем исключение
pEx->Delete();
Вернемся к блоку try. В нем приложение исполняет свою главную задачу - пытается установить соединение с сервером FTP. Адрес сервера FTP передается методу GetFtpConnection класса CInternetSession. Если соединение с сервером будет установлено, то этот метод вернет указатель на объект класса CFtpConnection и мы запишем его во временную переменную m_FtpConnection:
// Определяем указатель на объект класса CFtpConnection
CFtpConnection* m_FtpConnection = NULL;
// Пытаемся соединиться с сервером sServer
m_FtpConnection =
m_InternetSession -> GetFtpConnection( sServer );
Если попытка соединиться с сервером окажется неудачной, то метод GetFtpConnection вызовет исключение CInternetException и оно будет обработано соответствующим блоком catch.
В случае успешного соединения с сервером FTP, приложение отображает на экране соответствующее сообщение:
// Выводим сообщение об успешном соединении
cout << "Connect to FTP server established" << endl;
Больше приложение ConsoleFtp ничего не делает с сервером, поэтому закрываем соединение с сервером и удаляем соответствующий объект m_FtpConnection из памяти (этот объект создается методом GetFtpConnection):
// Закрываем соединение с сервером FTP
m_FtpConnection -> Close();
// Удаляем объект m_FtpConnection
delete( m_FtpConnection );
Перед окончанием работы приложения, завершаем сеанс связи, вызывая метод Close класса CInternetSession, а затем удаляем соответствующий объект из памяти, вызывая оператор delete:
// Завершаем сеанс связи
m_InternetSession -> Close();
// Удаляем объект m_InternetSession
delete(m_InternetSession);
Конечно, приложение ConsoleFtp демонстрирует только самые основные принципы использования классов WinInet. В следующем разделе мы приведем еще одно приложение для работы с серверами FTP. Приложение FtpView значительно сложнее чем ConsoleFtp. Оно позволяет пользователю просматривать структуру каталогов сервера FTP и названия записанных в них файлов. Несколько позже мы модифицируем приложение FtpView так, чтобы его можно было использовать для загрузки файлов и будем вносить в него небольшие изменения для демонстрации различных приемов работы с серверами FTP.
Устройство приложения ConsoleHttp
Рассмотрим теперь устройство приложения ConsoleHttp. Первая часть приложения ConsoleHttp практически совпадает с приложением ConsoleFtp, работа которого была рассмотрена нами выше в разделе “Устройство приложения ConsoleFtp”. Поэтому мы не будем на нем останавливаться. Скажем только что функция main сначала проверяет командную строку приложения. Если приложение было вызвано без параметров, или их больше чем один, то на экран выводится информация о формате вызова приложения.Если пользователь правильно указал в командной строке ConsoleHttp только один параметр, он записывается в строку sUrlAdress и отображается на экране:
CString sUrlAdress;
sUrlAdress = argv[1];
Далее строка sUrlAdress передается для обработки функции AfxParseURL, которая разбирает адрес, записанный в ней на составные части и помещает их в переменные dwServiceType, sServer, sObject и nPort:
DWORD dwServiceType; // Тип сервиса
CString sServer; // Имя сервера
CString sObject; // Имя объекта на который указывает адрес
INTERNET_PORT nPort; // Номер порта
if(!AfxParseURL( sUrlAdress, dwServiceType,
sServer, sObject, nPort))
{
cout << "AfxParseURL Error" << endl;
return -1;
}
В том случае если пользователь указал в командной строке приложения неправильный адрес, не соответствующий формату URL, то функция AfxParseURL возвращает нулевое значение. Наше приложение отображает сообщение об ошибке и завершается с кодом -1.
Далее приложение проверяет, что указанный адрес соответствует адресу сервера WWW. В этом случае тип сервиса dwServiceType должен быть равен значению AFX_INET_SERVICE_HTTP. Если это не так, например, вместо адреса сервера WWW вы указали адрес сервера FTP, который начинается с ftp://, то на экран будет выведено сообщение об ошибке и приложение завершится с кодом возврата -2:
if(dwServiceType != AFX_INET_SERVICE_HTTP)
{
cout << "URL Address not WWW server" << endl;
return -2;
}
Если адрес сервера указан верно, приложение создает сеанс связи с Internet. Для этого мы объявляем указатель на объект класса CInternetSession а затем создаем сам объект, используя функцию new:
// Указатель на объект класса CInternetSession
CInternetSession* m_InternetSession = NULL;
// Инициализируем сеанс работы с WinInet - создаем объект
// класса CInternetSession catch
m_InternetSession = new CInternetSession("WWW Connecter");
Конструктор класса CInternetSession имеет много параметров, но мы все их используем по умолчанию, кроме первого параметра, через который мы задаем имя сеанса - WWW Connecter.
Теперь можно попытаться соединиться с сервером и запросить с него файл. Для этого следует вызвать метод GetHttpConnection класса CInternetSession только что созданного объекта m_InternetSession. В случае ошибки этот метод может вызвать исключение CInternetException. Поэтому вызов метода GetHttpConnection мы помещаем в блок try и определяем для него соответствующий блок catch, обрабатывающий исключение CInternetException:
// Исключения, вызванные в этом блоке try обрабатываются
// следующим блоком catch
try
{
// . . .
}
catch (CInternetException* pEx)
{
// . . .
}
Обработчик исключения CInternetException вызывает метод GetErrorMessage для полученного исключения, который записывает во временный буфер szErr причину вызова исключения. Если текстовое описание исключения доступно, метод GetErrorMessage возвращает ненулевое значение. В этом случае мы отображаем полученный текст на экране.
Затем мы завершаем обработку исключения, вызывая для него метод Delete. Он удаляет объект, представляющий исключение из оперативной памяти:
// Удаляем исключение
pEx->Delete();
Рассмотрим блоку try более подробно. В нем приложение исполняет свою главную задачу - пытается установить соединение с сервером WWW. Адрес сервера WWW передается методу GetHttpConnection класса CInternetSession. Если соединение с сервером будет установлено, то этот метод вернет указатель на объект класса GetHttpConnection и мы запишем его во временную переменную m_HttpConnection:
// Определяем указатель на объект класса CHttpConnection
CHttpConnection* m_HttpConnection = NULL;
// Пытаемся соединиться с сервером sServer
m_HttpConnection =
m_InternetSession -> GetHttpConnection( sServer );
Далее приложение HttpConnection вызывает для установленного соединения метод OpenRequest. Первый параметр метода OpenRequest определяет действие. В данном случае в качестве этого параметра используется константа HTTP_VERB_HEAD, означающая что мы желаем только получить информацию о объекте указанном во втором параметре метода sObject:
CHttpFile* pFile=NULL;
. . .
pFile = m_HttpConnection ->
OpenRequest( CHttpConnection::HTTP_VERB_HEAD,
sObject
);
Если метод OpenRequest завершился успешно, он возвращает указатель на объект класса CHttpFile. Далее вы можете использовать методы класса CHttpFile чтобы узнать данные полученные с сервера. Если вам надо получить с сервера файл, вы должны использовать вместо команды HTTP_VERB_HEAD команду HTTP_VERB_GET.
Сначала для объекта pFile вызывается метод AddRequestHeaders. Он добавляет дополнительный заголовок headerInfo к запросу HTTP. Сам заголовок headerInfo содержит текстовую строку, в которой определяются тип файлов, которые может принимать наше приложение и его имя:
CString headerInfo(
_T("Accept: text/*\r\nUser-Agent: Console Http\r\n"));
if(pFile->AddRequestHeaders(headerInfo))
{
}
Если метод AddRequestHeaders завершился без ошибок, он возвращает ненулевое значение. В этом случае наше приложение вызывает метод SendRequest, который и передает сформированный запрос серверу WWW. Если запрос передан успешно, то метод SendRequest возвращает ненулевое значение. В противном случае возвращается нуль. В случае возникновения ошибок метод SendRequest может вызвать исключение CInternetException:
if(pFile->SendRequest())
{
}
Обратите внимание, что исключение CInternetException может также быть вызвано методом GetHttpConnection. Мы поместили эти методы в один блок try, поэтому исключение вызванное любым из них будет обработано одним и тем же блоком catch.
Метод SendRequest только передает запрос серверу. Чтобы получить от него ответ, мы обращаемся к методу QueryInfoStatusCode. Этот метод получает код состояния, связанный с последним запросом к серверу и записывает его во временную переменную dwReturnCode.
Если метод QueryInfoStatusCode отработал успешно, он возвращает ненулевое значение. В этом случае мы преобразуем значение dwReturnCode в текстовую форму и отображаем его на экране:
DWORD dwReturnCode;
if(pFile -> QueryInfoStatusCode(dwReturnCode))
{
CString sMessage;
sMessage.Format("%d",dwReturnCode);
cout << "SendRequest: " << sMessage << endl << endl;
}
Больше приложение ConsoleHttp ничего не делает с сервером, поэтому закрываем объект pFile и удаляем его из памяти:
pFile->Close();
if(pFile)
delete pFile;
Затем закрываем соединение с сервером и удаляем соответствующий объект m_HttpConnection из памяти (этот объект создается методом GetHttpConnection):
// Закрываем соединение с сервером WWW
m_HttpConnection -> Close();
// Удаляем объект m_HttpConnection
delete( m_HttpConnection );
Перед окончанием работы приложения, завершаем сеанс связи, вызывая метод Close класса CInternetSession, а затем удаляем соответствующий объект из памяти, вызывая функцию delete:
// Завершаем сеанс связи
m_InternetSession -> Close();
// Удаляем объект m_InternetSession
delete(m_InternetSession);
Весной 1997 года фирма Microsoft
Весной 1997 года фирма Microsoft выпустила новую версию Visual C++ 5.0, которая имеет три редакции - Lerning, Professional и Enterprise.Значительно изменился внешний вид оболочки Microsoft Visual C++. Меню и панели управления теперь сделаны в стиле навигатора Microsoft Internet Explorer.
В Visual C++ версии 5.0 Microsoft полностью заменила систему подсказки. Теперь для просмотра справочной базы данных используется встроенный навигатор Microsoft Internet Explorer. Соответственно изменился и формат файлов справочной базы данных. Теперь они имеют расширения IVI и IVT и представляют из себя компрессованые и собранные вместе файлы HTML, файлы изображений и исходные тексты примеров.
В Visual C++ 5.0 существенно расширены возможности использования технологии ActiveX. В дополнение к библиотеке MFC в состав Visual C++ включена новая библиотека классов - Active Template Library (ATL).
Библиотека ATL ориентирована на создание небольших по размеру и быстрых в исполнении органов управления ActiveX. Эти свойства играют особенно важную роль в среде Internet, критичной к объему передаваемой информации. Для облегчения работы с библиотекой ATL Visual C++ добавлены новые средства автоматизированной разработки приложений - “волшебник” ATL Object Wizard и ATL Application Wizard.
Существенно доработан компилятор Visual C++, который теперь создает более компактный и быстрый программный код. Появилась возможность разработки приложений, ориентированных на новейшие процессоры фирмы Intel - Pentium Pro и Pentium MMX с расширенным набором мультимедийных команд.
В Visual C++ 5.0 также реализована более полная поддержка стандарта ANSI языка программирования C++. Так, например, появился логический тип bool. Переменные этого типа могут принимать всего два значения true и false.
Версия Visual C++ 5.0 Enterprise Edition содержит дополнительные средства для работы с базами данных.
Претерпели изменения средства галереи компонент Visual C++. Теперь, чтобы добавить в проект новый компонент, вы должны выбрать из меню Project строку Add to Project, а затем из открывшегося временного меню строку Components and Controls. На экране появится диалоговая панель Components and Controls Gallery (галерея компонент и органов управления). Раньше эта диалоговая панель носила название Components Gallery (галерея компонент).
Сразу после установки Microsoft Visual C++ в списке компонент каталога Gallery расположены две папки. Папка Developer Studio Components находятся компоненты Microsoft, а в папке Registered ActiveX Controls органы управления ActiveX установленные в системе.
Вспомогательные файлы
Кроме уже описанных нами файлов в проект Look входят еще два исходных файла, содержащих программный код. Это файлы stdafx.cpp и включаемый файл stdafx.h. Исходный текст файла stdafx.cpp содержится в листинге 4.9. Как видите, он состоит из единственной директивы #include, включающей файл stdafx.h (листинг 4.10).Листинг 4.9. Файл stdafx.cpp
// Включаем файл stdafx.h, определенный в нашем приложении
#include “stdafx.h“
Файл stdafx.h задействует часто используемые включаемые системные файлы - afxwin.h, afxext.h, afxcmn.h и afxdisp.h. Эти файлы не изменяются, поэтому Microsoft Visual C++ компилирует их только один раз. За счет этого значительно сокращается время, затрачиваемое на повторное построение проекта.
Листинг 4.10. Файл stdafx.h
// Исключает редко используемые определения при обработке
// файлов заголовков
#define VC_EXTRALEAN
// Основные компоненты библиотеки MFC
#include
// Расширения MFC
#include
// Будут использоваться органы управления ActiveX
#include
#ifndef _AFX_NO_AFXCMN_SUPPORT
// Используется для органов управления Windows
#include
#endif // _AFX_NO_AFXCMN_SUPPORT
Методы класса CLookDlg и таблица сообщений этого класса определяются в файле LookDlg.cpp. Мы привели полный исходный текст файла LookDlg.cpp в листинге 4.4.
Взаимодействие с серверами FTP, WWW и Gopher
Чтобы выполнять с серверами Internet специфические операции, например создать на сервере FTP новый каталог или передать запрос на сервер WWW, вы должны сначала установить соединение с сервером данного типа. Для этого в состав класса CInternetSession включены три специальные метода - GetFtpConnection, GetHttpConnection и GetGopherConnection. Они, соответственно, открывают соединение с серверами FTP, WWW и Gopher, а также создают объект представляющий соединение с сервером соответствующего типа.Так метод GetFtpConnection, который выполняет соединение с сервером FTP, возвращает объект класса CFtpConnection, метод GetHttpConnection - объект класса СHttpConnection, а метод GetGopherConnection - объект класса СGopherConnection. Далее вы можете обращаться к методам этих классов и выполнять различные операции с сервером.
Взаимодействие с серверами Internet
У вас существует две принципиально разные возможности работы с серверами Internet. Первая предполагает, что вам надо только выполнить чтение объекта, адрес которого известен. Вторая возможность требует более сложных манипуляций, зато позволяет выполнять специфические операции, например поиск файлов на сервере FTP, создание и удаление каталогов, передача запросов на сервер WWW и т. д.Загрузка файла в фоновом режиме
Исходный вариант приложения FtpView и его модификация обладают еще одним очень неприятным недостатком. Во время загрузки файлов с сервера FTP интерфейс приложения блокируется практически полностью. Только по линейному индикатору, который используется в доработанном варианте приложения мы можем судить о том, что оно все же работает.В остальном складывается такое впечатление, что приложение “зависло”. Оно даже не перерисовывает свое окно и если временно переключится на другое приложение, окно которого перекроет окно FtpView, то изображение в окне FtpView будет испорчено. Только по окончании загрузки файла наше приложение спохватится и восстановит свое окно.
Как мы уже говорили ранее, эффекта зависания можно избежать, если использовать WinInet в асинхронном режиме или выполнять наиболее длительные операции по загрузке файлов в отдельной задаче.
Сейчас мы предлагаем вам испробовать второй путь и создать для загрузки файлов с сервера отдельную задачу, которая будет выполняться параллельно с основной задачей приложения. Таким образом, загрузка файла будет осуществляться в фоновом режиме и не будет блокировать пользовательский интерфейс приложения.
За основу мы взяли базовый вариант приложения FtpView, выполняющий загрузку файлов с сервера FTP при помощи метода GetFile. Мы постарались свести все изменения в исходных текстах приложения к минимуму. Вы должны будете только добавить три глобальные переменные, изменить метод FtpFileDownload класса CFtpViewDlg, и добавить функцию ThreadFileLoadProc.
Определение глобальных переменных добавьте в самом начале файла FtpViewDlg.cpp непосредственно после директив препроцессора. Все три новых глобальных переменных global_sFtpAddress, global_sFileName и global_sLocalFileName являются объектами класса CString. Эти переменные используются для передачи задаче, осуществляющей загрузку файла, адреса сервера FTP, пути каталога и имени исходного файла на сервере FTP, а также полного пути файла на диске локального компьютера в который будет записан файл с сервера:
//================== Глобальные переменные ===================
// Адрес сервера FTP
CString global_sFtpAddress = "";
// Полный путь файла на диске локального компьютера
CString global_sFileName = "";
// Путь каталога и имя файла на сервере FTP
CString global_sLocalFileName = "";
Задача, осуществляющая загрузку файла с сервера FTP, определяется функцией ThreadFileLoadProc. Как видите, мы не стали ее особенно усложнять и просто создали еще один сеанс связи с Internet, а затем в его рамках осуществили соединение с сервером FTP и получили с него заданный файл.
Адрес сервера FTP мы берем из глобальной переменной global_sFtpAddress, путь файла, который надо загрузить с сервера и его название - из переменной global_sFileName, а полный путь файла на диске локального компьютера, куда полученный файл должен быть записан - из переменной global_sLocalFileName:
//============================================================
// Задача, выполняющая загрузку файла с сервера FTP
//============================================================
UINT ThreadFileLoadProc(LPVOID param)
{
//=========================================================
// Инициализируем сеанс связи с Internet
//=========================================================
CFtpConnection* m_FtpConnection; // Сервер FTP
CInternetSession* m_InternetSession; // Сеанс связи
// Создаем сеанс связи с Internet, указываем в качестве
// имени программы-клиента строку FtpProcess
m_InternetSession = new CInternetSession("FtpProcess");
// В случае ошибки отображаем сообщение и завершаем
// задачу с кодом завершения 1
if(!m_InternetSession)
{
AfxMessageBox("New Session Error", MB_OK);
return 1;
}
// Инициализируем указатель m_FtpConnection
m_FtpConnection = NULL;
// Пытаемся соединиться с сервером FTP
try
{
// Соединяемся с сервером FTP. Эта операция
// может вызвать исключение CInternetException
m_FtpConnection = m_InternetSession ->
GetFtpConnection(global_sFtpAddress);
}
catch (CInternetException* pEx)
{
// Выводим сообщение об ошибке
AfxMessageBox("GetFtpConnection Error");
// Удаляем исключение
pEx->Delete();
// Обнуляем указатель m_FtpConnection
m_FtpConnection = NULL;
}
if(m_FtpConnection != NULL)
{
BOOL fResult;
// Загружаем файл с сервера
fResult = m_FtpConnection ->
GetFile(global_sFileName.GetBuffer(MIN_LEN_BUF),
global_sLocalFileName.GetBuffer(MIN_LEN_BUF),
FALSE
);
// Закрываем соединение с сервером
m_FtpConnection -> Close();
delete m_FtpConnection;
// После успешного завершения загрузки выводим сообщение
if( fResult )
AfxMessageBox("File load complited:" +
global_sFileName);
}
// Завершаем сеанс связи с Internet
m_InternetSession -> Close();
delete m_InternetSession;
return 0;
}
Функция ThreadFileLoadProc выполняет загрузку файла с сервера FTP в файл на диске локального компьютера. Надо только заполнить глобальные переменные global_sFtpAddress, global_sFileName и global_sLocalFileName, а затем запустить функцию ThreadFileLoadProc как отдельную задачу. Для этого измените метод FtpFileDownload класса CFtpViewDlg как это показано ниже:
BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )
{
BOOL fResult;
CString sLocalFileName;
// Определяем объект класса CFileDialog, представляющий
// стандартную диалоговую панель Save As, в которой
// по умолчанию выбрано имя файла sFileName
CFileDialog mFileOpen(FALSE, NULL, sFileName);
// Отображаем диалоговую панель Open и позволяем
// пользователю выбрать с помощью нее один файл
int result = mFileOpen.DoModal();
// Проверяем как была закрыта диалоговая панель Open -
// по нажатию кнопки OK или Cancel
if(result == IDCANCEL)
{
// Если пользователь отказался от выбора файлов и
// нажал кнопку Cancel отображаем соответствующее
// сообщение и возвращаем значение FALSE
AfxMessageBox("File not selected");
return FALSE;
}
// Если пользователь нажал кнопку OK
else if(result == IDOK)
{
// Записываем полный путь файла на диске локального
// компьютера
global_sLocalFileName = mFileOpen.GetPathName();
// Определяем путь и имя файла, который надо загрузить
// с сервера FTP
global_sFileName = sCurentDirectory + "/" + sFileName;
// Запоминаем адрес сервера FTP
global_sFtpAddress = m_FtpAddress;
// Запускаем новую задачу, определенную функцией
// ThreadFileLoadProc
AfxBeginThread( ThreadFileLoadProc, NULL,
THREAD_PRIORITY_NORMAL);
}
return fResult;
}
Мы не будем подробно описывать функцию ThreadFileLoadProc и новый вариант метода FtpFileDownload класса CFtpViewDlg. В них используются те же приемы, что продемонстрированы в методе FtpFileDownload класса CFtpViewDlg базового варианта приложения FtpView.
Загрузка файлов с сервера FTP
Приложение FtpView, рассмотренное нами в предыдущем разделе, позволяет только просматривать структуру каталогов сервера FTP. С его помощью вы не сможете загрузить файл с сервера на диск локального компьютера или наоборот, записать файл на сервер FTP. В этом разделе мы расширим возможности приложения FtpView, так чтобы его можно было использовать для загрузки файлов с сервера.Приложение FtpView построено таким образом, что при выборе из списка файла, метод OnDblclkFtpList отображает на экране предупреждающее сообщение и больше ничего не делает. Добавим к классу CFtpViewDlg новый метод FtpFileDownLoad, которое будет выполнять загрузку файла с данным именем. Тогда нам останется немного исправить метод OnDblclkFtpList и приложение для загрузки файлов готово.
Запустите приложение
Если компьютер настроен для работы с Internet, но в настоящий момент соединение не установлено, подключитесь к Internet. Запустите приложение Look (рис. 4.12). Сразу после того, как на экране появится панель приложения Look, вы можете перейти к просмотру серверов WWW и FTP. Для этого выберите адрес сервера из списка Address или введите его вручную. Нажмите кнопку Navigate. Через некоторое время в окне просмотра появится выбранная вами страница. В зависимости от множества причин, таких как скорость соединения вашего компьютера с сервером поставщика услуг Internet, загруженности его сервера и сервера, с которым вы соединяетесь, возможны значительные задержки при загрузке страниц из сети.Когда страничка WWW или FTP появится в окне приложения, вы можете перейти к просмотру других страниц, используя приложение Look как действующую модель Internet Explorer в натуральную величину.

Рис. 4.12. Приложение Look
Работа с информацией: Cистемы - Технологии - Рынок
- Анализ информационных систем
- Методы информационных систем
- Интернет как информационная система
- Искусственный интеллект в информационных системах
- Обработка информации информационными системами
- Информационные системы в офисе
- Управление информационными системами
- Технологии информационных систем
- Теория информационных систем
- Почта - информационная система
- Outlook и информационные системы
- Информационный рынок
- Информационный рынок - IT
- Технологии информационного рынка
- Безопасность на информационном рынке
ALT="MS Internet Explorer v3.0" BORDER=0>




