Разработка приложений для 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!


На этом сервере вы можете найти подробную информацию
   о наших книгах из серий "Персональный компьютер.
   Шаг за шагом" и "Библиотека системного программиста":

  • аннотации и оглавления;
  • исходные тексты программ, опубликованных в книгах;
  • наши планы на будущее;
  • список ошибок, замеченных в наших книгах;
  • информацию о наших друзьях;

  • бесплатные книги, доступные в режиме online

    Эти серии книг предназначенны для самого широкого
    круга людей, чья работа связана с компьютерами.
    Мы подготовили для вас и кое-что еще!


    Эту страницу лучше всего просматривать навигаторами MS Internet Explorer v3.0 или Netscape Navigator

       ALT="MS Internet Explorer v3.0" BORDER=0>

    Netscape Navigator

       ALIGN=bottom>

    Посылайте ваши комментарии по адресу
       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).
    Ваш собственный Internet Explorer
    Рис. 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, или другой навигатор, установленный в компьютере, и вы сможете просмотреть выбранный ресурс.
    Ссылки на ресурсы Internet
    Рис. 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

    Доменная система имен

    Много полезной информации располагается на сервере консорциума World Wide Web по адресу http://www.w3c.org/. Обязательно посетите этот сервер и просмотрите расположенные на нем материалы.

    ¨ Путешествуя в сети 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

    Шаблоны новых методов будут добавлены к файлу LookDlg.cpp, содержащему определения всех методов класса CLookDlg. Вы должны внести в них изменения в соответствии с листингом 4.8.

    Определение класса 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 вы сможете легко добавить к классам новые элементы данных и новые методы.
    Исходные тексты приложения FtpView
    Рис. 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. На ней вы можете просмотреть структуру классов нашего приложения.
    Как устроено приложение FtpView
    Рис. 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).
    Класс CFtpViewDlg
    Рис. 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 не имеет собственной переменной, так как она нам не понадобится.

    Класс CFtpViewDlg

    Рис. 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).

    Класс CFtpViewDlg

    Рис. 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. Этот класс содержит большое количество методов, позволяющих управлять навигатором - переходить к просмотру заданных страниц, обновлять отображаемую информацию и т. д.
    Класс CLookDlg и органы управления панели IDD_LOOK_DIALOG
    Рис. 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   // Используется интерфейсом WinInet
    #include       // Содержит определение класса CFileFind

    Кнопки “Вперед” и “Назад”

    В нашем варианте навигатора отсутствуют кнопки, позволяющие вернуться к просмотру страниц 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 сервер
    <
    Конструктор класса CInternetSession

    Откройте Control Panel и выберите приложение Internet, которое предназначено для настройки различных параметров доступа к сети Internet

    На экране появится диалоговая панель Internet Properties, состоящая из нескольких страниц (рис. 1.2). Выберите страницу Connection, отвечающую за подключение к Internet.

    Конструктор класса CInternetSession

    Рис. 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

    Операции по загрузке информации выполняются только из кэша. В том случае, если запрашиваемая информация в кэше отсутствует, возвращается ошибка

    Если соединение не может быть открыто, то конструктор класса CInternetSession вызывает исключение CInternetException с помощью глобальной функции AfxThrowInternetException. Мы расскажем о классе CInternetException, его методах и функции AfxThrowInternetException в разделе “Класс CInternetException”.

    Конструктор класса 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. Файлы выделяются пиктограммой Метод OnDblclkFtpList, а каталоги пиктограммой Метод OnDblclkFtpList и строкой 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

    Эти константы определены в классе CHttpConnection следующим образом:

    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

    Метод OpenRequest возвращает указатель на объект класса CHttpFile, соответствующий запрашиваемому объекту.

    Метод 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

    Как видите, метод OpenURL может вернуть указатель на объекты класса CStdioFile, CGopherFile, CInternetFile или CHttpFile, используя указатель на объект класса CStdioFile. Такая многофункциональность метода OpenURL возможна благодаря тому, что класс CStdioFile является базовым классом для классов CGopherFile, CInternetFile и CHttpFile:

    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++

    Когда вы вызываете метод OpenURL, вы можете указать дополнительный заголовок запроса HTTP, передаваемый серверу. Дополнительный заголовок может быть представлен в формате RFC822, MIME или HTTP.

    По умолчанию дополнительный заголовок не указывается. Указатель на строку, содержащую дополнительный заголовок запроса 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).

    Недостатки приложения FtpView

    Рис. 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.
    Орган управления Microsoft Web Browser
    Рис. 4.6. Ресурсы приложения Look
    Загрузите в редактор ресурсов диалоговую панель IDD_LOOK_DIALOG приложения Look. Если панель инструментов Controls не появилась на экране, откройте ее. Для этого выберите из меню View главного окна Microsoft Visual C++ строку Toolbars. На экране появится диалоговая панель Toolbars (рис. 4.7). Найдите в списке Toolbars переключатель Controls и включите его. Затем нажмите кнопку Close.
    Если в списке Toolbars вы не смогли обнаружить переключатель Controls, проверьте, загружена ли диалоговая панель в окно редактирования. Панели управления Microsoft Visual C++ являются контекстно-зависимыми. При работе с различными объектами доступны разные панели управления.
    Орган управления Microsoft Web Browser
    Рис. 4.7. Диалоговая панель Toolbars
    Измените диалоговую панель приложения Look в соответствии с листингом 4.х. На рисунке 4.8 мы представили вид диалоговой панели IDD_LOOK_DIALOG в редакторе ресурсов.
    Орган управления Microsoft Web Browser
    Рис. 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. В ней появилась новая пиктограмма Орган управления Microsoft Web Browser (рис. 4.9), которая и представляет Microsoft Web Browser Control. Если в диалоговой панели Toolbars установлен переключатель Show ToolTips, который включает режим кратких подсказок, и вы поместите на указатель мыши на пиктограмму Орган управления Microsoft Web Browser, то через некоторое время появится название этого органа управления - Microsoft Web Browser.

    Орган управления 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 " << endl;

          cout << "  - URL address of FTP" << endl << endl;

          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.

    Приложение ConsoleFtp

    Рис. 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 с таким именем не существует.

    Приложение ConsoleHTTP

    Рис. 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 " << endl;

          cout << "  - URL address of WWW" << endl << endl;

          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.

    Приложение ConsoleHTTP

    Рис. 3.2. Приложение ConsoleHttp запрашивает страницы http://www.frolov.ru/~frolov и http://www.glasnet.ru/~frolov

    Приложение FtpView

    Навигатор Microsoft Internet Explorer можно использовать для просмотра серверов FTP и загрузки с них файлов на локальный диск компьютера. Если вы запустите навигатор и введете для просмотра адрес какого-нибудь сервера FTP, например сервера Microsoft, то в окне навигатора будет показан список файлов и каталогов сервера (рис. 2.2).
    Приложение FtpView
    Рис. 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, которые вы можете использовать в ваших приложениях.

    Приложение Look

    Рис. 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++.

    Приложение Look

    Орган управления Microsoft Web Browser Control предоставляет возможность просмотра WWW страниц.

    Для добавления к проекту органа управления Microsoft Web Browser Control нажмите кнопку Insert, после того как вы выберите соответствующую пиктограмму. На экране появится диалоговая панель Confirm Classes (рис. 4.3).

    Список классов, которые будут созданы для работы с органом управления Microsoft Web Browser Control содержит название только одного класса - CWebBrowser. Обратите внимание на то, что переключатель отображаемый перед названием класса CWebBrowser должен быть включен.

    В поле Class name отображается имя выбранного класса. Вы можете изменить его по своему усмотрению. Имя базового класса для класса CWebBrowser отображается в поле Base class. Данное поле выполняет исключительно информационные функции, так как изменить имя базового класса в панели Confirm Classes невозможно. В качестве базового класса для CWebBrowser выступает класс CWnd.


    Имена включаемого файла и файла реализации, в которых будут помещены определение класса CWebBrowser и его методов, отображаются в полях Headr file и Implementation file. По умолчанию используются имена файлов WebBrowser.h и WebBrowser.cpp. В случае необходимости вы можете указать для этих файлов другие имена.

    Приложение Look

    Рис. 4.3. Диалоговая панель Confirm Classes

    Когда все поля диалоговой панели Confirm Classes заполнены, нажмите кнопку OK. Для нашего приложения мы оставили имя класса органа управления Microsoft Web Browser Control, а также имена включаемого файла и файла реализации без изменения.

    После того, как вы добавите в проект орган управления Microsoft Web Browser, просмотрите список входящих в него файлов. Для этого откройте окно Project Workspace и перейдите на страницу FileView (рис. 4.4).

    Приложение Look

    Рис. 4.4. Исходные файлы приложения Look

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

    Приложение Look

    Рис. 4.5. Классы приложения Look

    В состав проекта Look входят всего три класса, коротко описанные в следующей таблице.

    Класс

    Описание

    CLookApp

    Главный класс приложения Look

    CLookDlg

    Класс, управляющий диалоговой панелью приложения

    CWebBrowser

    Класс органа управления Microsoft Web Browser Control


    Приложение Parse

    В этом разделе мы представим вашему вниманию приложение Parse. Оно использует функцию AfxParseURL для разбора на составные части адресов URL, которые пользователь может вводить в диалоговой панели приложения (рис. 1.3).
    Приложение Parse не выполняет соединение с Internet и вы можете его запустить на компьютере, не имеющем ни модема, ни сетевой карты, и не подключенном к сетям Internet и Intranet. Конечно, пользы от такого приложения не много, но мы предлагаем вам с ним поработать, чтобы лучше разобраться со структурой адресов URL.
    Приложение Parse
    Рис. 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.

    Процедура загрузки файла с сервера FTP

    Рис. 2.14. Выбор имени файла на локальном компьютере

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

    Процедура загрузки файла с сервера FTP

    Рис. 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.
    Работа приложения FtpView
    Рис. 2.11. Приложение FtpView
    Список содержимого текущего каталога сервера отображается в таблице, состоящей из четырех столбцов - Name, Length, Date и Time. В столбце Name отображаются имена каталогов и файлов, расположенных в данном каталоге. Остальные три столбца - Length, Date и Time включают информацию о размере, дате и времени создания или изменения соответствующего каталога или файла. Каталоги выделяются пиктограммой Работа приложения FtpView, расположенной  перед его именем и строкой Dir в столбце Length, а файлы только пиктограммой Работа приложения FtpView.
    Чтобы войти в каталог, надо сделать двойной щелчок мышью по его названию. Приложение попытается просмотреть содержимое указанного вами каталога и выведет его в таблице диалоговой панели приложения. При этом содержимое старого каталога будет скрыто. Чтобы вернуться в каталог верхнего уровня, надо нажать кнопку On top. Таким образом вы можете просматривать структуру каталогов сервера FTP.
    Если выберать из списка не каталог, а файл, то на экране появится предупреждающее сообщение и вы останетесь в текущем каталоге. На рисунке 2.12 показано сообщения, которое появляется при выборе файла. В данном случае мы выбрали файл с именем README.DOC.
    Работа приложения FtpView
    Рис. 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.
    Ресурсы приложения FtpView
    Рис. 2.3. Редактирование диалоговой панели FtpView
    Список List Control может отображать информацию в различных форматах. Это могут быть пиктограммы большого или маленького размера с подписями, отсортированные различным образом, или табличная информация разделенная на несколько колонок. Мы будем использовать последний из перечисленных форматов.
    Откройте панель свойств списка List Control Properties. Мы показали ее на рисунке 2.4. Перейдите на страницу Styles. Из списка View выберите строку Report, которая определяет, что список будет отображать табличную информацию. Из списка Align выберите строку Left. Она задает выравнивание строк, отображаемых в списке, по левой границе. Вы также можете определить будут ли сортироваться строки, отображаемые в списке. Мы выбрали прямой порядок сортировки. Вы, в принципе, можете использовать обратный порядок сортировки или отказаться от сортировки совсем.
    Ресурсы приложения FtpView
    Рис. 2.4. Свойства списка IDC_FTP_LIST
    В нижней части диалоговой панели разместите текстовую надпись Directory и поле редактирования для отображения названия текущего каталога с идентификатором IDI_DIRECTORY. Поле IDI_DIRECTORY будет использоваться только для вывода текста, поэтому откройте диалоговую панель свойств этого органа управления, перейдите на страницу Styles и включите переключатель Read-only (рис. 2.5).

    Ресурсы приложения FtpView

    Рис. 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 в два раза.

    Пиктограмма

    Идентификатор пиктограммы

    Имя файла

    Ресурсы приложения FtpView

    IDI_FILE

    file.ico

    Ресурсы приложения FtpView

    IDI_DIRECTORY

    director.ico

    Ресурсы приложения FtpView

    IDR_MAINFRAME

    russian.ico

    На рисунке 2.6 мы показали страницу ResourceView диалоговой панели Project Workspace. Так она будет выглядеть после того, как вы добавите в проект все необходимые нам ресурсы.

    Ресурс VS_VERSION_INFO, который описывает версию приложения, нами не используется и мы оставляем его без изменения таким, каким он создан MFC AppWizard.

    Ресурсы приложения FtpView

    Рис. 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 " << endl;
       cout << "  - URL address of FTP" << endl << endl;
       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    // Common Controls
    #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истемы - Технологии - Рынок