1. Интерфейс 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, которые интегрируют в себе все возможности этого программного интерфейса.

Классы 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 <afxinet.h>  // Используется интерфейсом WinInet
#include <afx.h>      // Содержит определение класса CFileFind

Общие принципы устройства приложений WinInet

Приложения, взаимодействующие с серверами Internet посредством программного интерфейса WinInet и надстроенных над ним классов библиотеки MFC, должны придерживаться стандартных сценариев установления связи и обмена данными, которые мы рассмотрим в данном разделе.

Сеанс связи

Любое приложение, использующее классы и методы WinInet, должно, в первую очередь, инициализировать сеанс работы с интерфейсом WinInet или, другими словами, сеанс связи. Для этого следует создать объект класса CInternetSession. Одно приложение может установить несколько сеансов связи, если того требует выполняемая им задача.

Только после этого в рамках данного сеанса возможна дальнейшая работа с серверами Internet, будь то установление связи с серверами WWW или FTP, чтение и запись файлов, поиск файлов по заданному адресу и т. д. Единственное что вы можете делать с программным интерфейсом WinInet до того, как создан сеанс - это выполнять разбор адресов URL при помощи глобальной функции AfxParseURL. Описание этой функции и пример ее использования вы найдете в разделе “Адреса URL”.

По окончании работы следует закрыть сеанс связи и удалить соответствующий объект класса CInternetSession, если он был создан динамически.

Настройка сеанса связи

Как только создан объект класса CInternetSession, представляющий сеанс связи, вы можете воспользоваться методами этого класса, чтобы изменить некоторые его характеристики. Так, например, вы можете установить ограничение на выполнение некоторых операций, которые могут отнять много времени. Для этого вы можете использовать методы SetOption и QueryOption. Заметим, однако, что в большинстве случаев изменять характеристики сеанса связи нет никакой необходимости и вы можете сразу приступить к выполнению основной задачи - соединиться с сервером WWW или FTP, просмотреть структуру его каталогов, прочитать или записать файл.

Обработка ошибок

В случае возникновения ошибок, многие методы классов WinInet вызывают исключение CInternetException. Чтобы правильно обрабатывать эти исключения, вы должны поместить методы, которые могут вызвать исключение, в блок try и определить соответствующий блок catch, выполняющий его обработку.

Взаимодействие с серверами Internet

У вас существует две принципиально разные возможности работы с серверами Internet. Первая предполагает, что вам надо только выполнить чтение объекта, адрес которого известен. Вторая возможность требует более сложных манипуляций, зато позволяет выполнять специфические операции, например поиск файлов на сервере FTP, создание и удаление каталогов, передача запросов на сервер WWW и т. д.

Чтение файла с серверов сети Internet

В этом случае вы можете сразу вызвать метод OpenURL класса CInternetSession. Он выполнит соединение с указанным ему сервером и создаст объект класса CInternetFile, соответствующий необходимому вам файлу.

Далее вы можете воспользоваться методами класса CInternetFile, чтобы прочитать данный файл с сервера на локальный компьютер. Для этой цели вы можете вызвать метод Read, который читает файл и записывает считанные данные в буфер, или метод ReadString, который считывает из файла текстовую строку, ограниченную символом перевода строки.

Описанный метод получения файла не позволяет выполнить обратную задачу - записать файл на сервер FTP. Серверы WWW и Gopher такую операцию не допускают в любом случае. От них вы можете только получать информацию.

Взаимодействие с серверами FTP, WWW и Gopher

Чтобы выполнять с серверами Internet специфические операции, например создать на сервере FTP новый каталог или передать запрос на сервер WWW, вы должны сначала установить соединение с сервером данного типа. Для этого в состав класса CInternetSession включены три специальные метода - GetFtpConnection, GetHttpConnection и GetGopherConnection. Они, соответственно, открывают соединение с серверами FTP, WWW и Gopher, а также создают объект представляющий соединение с сервером соответствующего типа.

Так метод GetFtpConnection, который выполняет соединение с сервером FTP, возвращает объект класса CFtpConnection, метод GetHttpConnection - объект класса СHttpConnection, а метод GetGopherConnection - объект класса СGopherConnection. Далее вы можете обращаться к методам этих классов и выполнять различные операции с сервером.

Асинхронный режим WinInet

К сожалению, классы WinInet имеют один недостаток. Большинство операций по взаимодействию с сетью Internet занимают много времени. Даже процедура соединения с сервером может занять несколько десятков секунд. Мы уже не говорим о загрузки с серверов FTP файлов большого размера. Даже если вы имеете соединение с Internet через высокоскоростной модем, получение файла с размерами в несколько мегабайт займет несколько десятков минут.

В самом простом случае классы WinInet используются в синхронном режиме, вызывая соответствующие методы классов непосредственно из основной задачи приложения. Естественно, пользовательский интерфейс приложения в этом случае блокируется. Мало того, что пользователь не сможет работать с таким приложением во время загрузки файла, если вы переключитесь в это время на другое приложение, открыв его окно поверх окна приложения работающего с Internet, а затем попытаетесь переключится обратно, то окно приложения не будет восстановлено.

Иллюстрацией вышесказанного может быть приложение FtpView, описанное нами в разделе “Загрузка файлов с сервера FTP”. Соединитесь с его помощью с каким-либо сервером FTP и попытайтесь загрузить файл большого размера. Вы увидите что в этом случае пользовательский интерфейс приложения блокируется и окно приложения не перерисовывается.

Существует две возможности выхода из этой ситуации.

Во-первых, можно организовать в рамках приложения еще одину задачу, и все вызовы методов WinInet, которые могут занять много времени выполнять именно из нее. При этом основная задача приложения останется свободеной и пользовательский интерфейс блокироваться не будет.

Во-вторых, можно использовать WinInet в асинхронном режиме. При этом методы классов WinInet будут сразу возвращать управление еще до того как соответствующая операция завершится. Возникает вопрос, как же приложение узнает о том что длительная асинхронная операция наконец-то завершилась и можно продолжить работу приложения далее?

Программный интерфейс WinInet извещает приложение о завершении асинхронной операции через специальную функцию обратного вызова. Если вы используете классы MFC, управляющие WinInet, вы можете воспользоваться виртуальным методом OnStatusCallback класса CInternetSession, который вызывается функцией обратного вызова WinInet.

Чтобы воспользоваться методом 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/.

Класс 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

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

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

Конструктор класса CInternetSession имеет следующий прототип:

CInternetSession(
   LPCTSTR pstrAgent = NULL, 
   DWORD dwContext = 1,
   DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS, 
   LPCTSTR pstrProxyName = NULL, 
   LPCTSTR pstrProxyBypass = NULL, 
   DWORD dwFlags = 0 
);

Посмотрите, как определен конструктор класса CInternetSession в исходных текстах библиотеки MFC. Вы найдете соответствующий программный код в файле Inet.cpp.

Конструктор класса CInternetSession вызывает единственную функцию программного интерфейса WinInet - InternetOpen, которая выполняет инициализацию WinInet.

Параметры конструктора класса CInternetSession передаются непосредственно функции InternetOpen:

m_dwContext = dwContext;
if (pstrAgent == NULL)
   pstrAgent = AfxGetAppName();

m_hSession = InternetOpen(pstrAgent, dwAccessType,
   pstrProxyName, pstrProxyBypass, dwFlags);

Вернемся теперь к обсуждению параметров конструктора. Все параметры конструктора класса CInternetSession являются необязательными и при создании объекта данного класса вы их можете не указывать.

Через параметр pstrAgent можно явным образом указать название приложения, или программного модуля, работающего с WinInet. Это название используется протоколом HTTP, и передается серверу в качестве имени пользовательского агента.

Если вызвать конструктор класса CInternetSession без параметров или указать в качестве параметра pstrAgent значение NULL, то конструктор самостоятельно определит имя приложения. Для этого конструктор использует глобальную функцию AfxGetAppName (см. фрагмент из файла Inet.cpp представленный выше).

Параметр dwContext определяет идентификатор контекста. Созданный объект будет ассоциироваться с данным идентификатором контекста.

Параметр dwAccessType определяет тип доступа к сети. Можно использовать одно из значений, перечисленных в следующей таблице:

Константа Тип доступа
INTERNET_OPEN_TYPE_PRECONFIG Используется тип доступа, указанный в регистрационной базе Windows
INTERNET_OPEN_TYPE_DIRECT Прямой доступ к Internet
INTERNET_OPEN_TYPE_PROXY Доступ через proxy сервер

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

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

Рис. 1.2. Диалоговая панель Internet Properties

Если включен переключатель Connect to the Internet as needed в группе Dialing, значит ваш компьютер подключается к сети через модем с использованием Dial-Up Networking. Если включен переключатель Connect through a proxy server, значит вы подключены к сети через специальный proxy сервер. Настройки этого сервера вы можете просмотреть, нажав кнопку Setting. В случае, если оба эти переключателя отключены, значит вы подключены к сети напрямую.

Константы PRE_CONFIG_INTERNET_ACCESS и INTERNET_OPEN_TYPE_PRECONFIG определены в файле wininet.h. Ниже мы привели соответствующий фрагмент этого файла:

//
// access types for InternetOpen()
//

#define INTERNET_OPEN_TYPE_PRECONFIG   0   // use registry configuration
#define INTERNET_OPEN_TYPE_DIRECT      1   // direct to net
#define INTERNET_OPEN_TYPE_PROXY       3   // via named proxy

#define PRE_CONFIG_INTERNET_ACCESS  INTERNET_OPEN_TYPE_PRECONFIG
#define LOCAL_INTERNET_ACCESS       INTERNET_OPEN_TYPE_DIRECT
#define GATEWAY_INTERNET_ACCESS     2   // Internet via gateway
#define CERN_PROXY_INTERNET_ACCESS  INTERNET_OPEN_TYPE_PROXY

Как видите, значения констант PRE_CONFIG_INTERNET_ACCESS и INTERNET_OPEN_TYPE_PRECONFIG полностью совпадают.

Если тип доступа, определен параметром dwAccessType как INTERNET_OPEN_TYPE_PROXY, то через параметр pstrProxyName следует указать имя proxy сервера, а через параметр pstrProxyBypass - указатель на строку со списком адресов дополнительных серверов. Вы можете указать в качестве параметра pstrProxyBypass значение NULL. При этом список серверов будет взят из регистрационной базы данных Windows.

Если используются другие типы доступа, то в качестве параметра dwAccessType надо указать значение NULL. Параметр pstrProxyBypass в этом случае значения не имеет.

Последний параметр конструктора dwFlags определяет различные характеристики, перечисленные в следующей таблице:

Константа Описание
INTERNET_FLAG_DONT_CACHE Данные, получаемые WinInet из сети кэшируются в специальном каталоге на жестком диске локального компьютера. В случае если пользователь часто запрашивает у WinInet одни и те же файлы, то для ускорения они могут браться не из сети, а из кэша. Константа INTERNET_FLAG_DONT_CACHE запрещает кэшировть принимаемые данные
INTERNET_FLAG_ASYNC Устанавливает асинхронный режим работы WinInet. Последующие операции с объектом могут завершится с ошибкой ERROR_IO_PENDING. Когда операция закончится, функция обратного вызова, будет вызвана с кодом INTERNET_STATUS_REQUEST_COMPLETE
INTERNET_FLAG_OFFLINE Операции по загрузке информации выполняются только из кэша. В том случае, если запрашиваемая информация в кэше отсутствует, возвращается ошибка

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

Метод 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. В режиме отладки также будет выдано сообщение.

Через параметр 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. Для этих целей вы должны использовать другие классы (чтобы открыть соединение определенного вида).

Метод 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, которое вы должны обрабатывать в своем приложении.

Метод EnableStatusCallback

Чтобы разрешить программному интерфейсу WinInet вызывать метод OnStatusCallback, необходимо воспользоваться методом EnableStatusCallback, прототип которого мы представили ниже:

BOOL 
EnableStatusCallback( BOOL bEnable = TRUE );

throw ( CInternetException );

Метод EnableStatusCallback имеет только один параметр, который разрешает или запрещает использование метода OnStatusCallback. По умолчанию параметр bEnable содержит значение TRUE, которое разрешает его использование.

Метод EnableStatusCallback возвращает ненулевое значение в случае успешного завершения или ноль в случае ошибки. Данный метод может вызывать исключение CInternetException. Вы можете определить причину исключения, воспользовавшись методами класса CInternetException.

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

INTERNET_STATUS_RESOLVING_NAME

Определяет IP-адрес имени, записанного в параметре lpvStatusInformation.

INTERNET_STATUS_NAME_RESOLVED

В буфере lpvStatusInformation находится IP-адрес сервера, имя которого вы указали.

INTERNET_STATUS_CONNECTING_TO_SERVER

Выполняется соединение по адресу сокета, указанного в буфере lpvStatusInformation.

INTERNET_STATUS_CONNECTED_TO_SERVER

Установлено соединение по адресу сокета, указанного в буфере lpvStatusInformation.

INTERNET_STATUS_SENDING_REQUEST

Передается информационный запрос на сервер. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_REQUEST_SENT

Информационный запрос передан на сервер. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_RECEIVING_RESPONSE

Ожидает ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_RESPONSE_RECEIVED

Получен ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_CLOSING_CONNECTION

Соединение с сервером закрывается. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_CONNECTION_CLOSED

Соединение с сервером закрыто. Параметр lpvStatusInformation не используется.

INTERNET_STATUS_HANDLE_CREATED

Функция InternetConnect из программного интерфейса WinInet создала новый идентификатор.

INTERNET_STATUS_HANDLE_CLOSING

Данный идентификатор удален.

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.

AFX_MANAGE_STATE( AfxGetStaticModuleState() );

Метод 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, описаны ниже.

INTERNET_OPTION_CALLBACK

Адрес функции обратного вызова, установленной для данного сеанса.

INTERNET_OPTION_CONNECT_TIMEOUT

Время, отведенное на установление соединения. Задается в миллисекундах. Если соединение не устанавливается в течении данного времени, оно отменяется. По умолчанию время на соединение никак не ограничено.

INTERNET_OPTION_CONNECT_RETRIES

Количество попыток соединения. В случае неудачной попытки соединения выполняется соответствующее количество повторных попыток. По умолчанию выполняется 5 попыток.

INTERNET_OPTION_CONNECT_BACKOFF

Задержка в миллисекундах между повторными попытками соединения.

INTERNET_OPTION_CONTROL_SEND_TIMEOUT

Время, отведенное для передачи запроса, не связанного с передачей данных. Задается в миллисекундах. По умолчанию время на передачу запроса не ограничено

INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT

Время, отведенное для приема запроса, не связанного с получением данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.

INTERNET_OPTION_DATA_SEND_TIMEOUT

Время, отведенное для передачи запроса с данными. Задается в миллисекундах. По умолчанию время на передачу запроса не ограничено.

INTERNET_OPTION_DATA_RECEIVE_TIMEOUT

Время, отведенное для запроса на прием данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.

INTERNET_OPTION_HANDLE_TYPE

Тип идентификатора Internet, например INTERNET_HANDLE_TYPE_CONNECT_FTP или INTERNET_HANDLE_TYPE_HTTP_REQUEST.

INTERNET_OPTION_CONTEXT_VALUE

Идентификатор контекста, связанный с данным идентификатором Internet.

INTERNET_OPTION_READ_BUFFER_SIZE

Размер буфера, используемого для чтения данных с сервера FTP.

INTERNET_OPTION_WRITE_BUFFER_SIZE

Размер буфера, используемого для передачи данных на сервер FTP.

INTERNET_OPTION_ASYNC_ID

Идентификатор последнего асинхронного запроса, который выполнен в данной задаче.

INTERNET_OPTION_ASYNC_PRIORITY

Приоритет асинхронной операции загрузки данных.

INTERNET_OPTION_PARENT_HANDLE

Родительский идентификатор.

INTERNET_OPTION_KEEP_CONNECTION

Определяет, используется ли данным идентификатором уже существующее соединение.

INTERNET_OPTION_USERNAME

Имя пользователя, связанное с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.

INTERNET_OPTION_PASSWORD

Пароль, связанный с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.

INTERNET_OPTION_REQUEST_FLAGS

Информация о текущем процессе загрузки. Если загрузка идет из кэш-памяти, то возвращается флаг INTERNET_REQFLAG_FROM_CACHE.

INTERNET_OPTION_EXTENDED_ERROR

Код ошибки программного интерфейса Winsock, который вызвал ошибку ERROR_INTERNET_ в данной задаче приложения.

Если метод QueryOption завершился без ошибок, он возвращает значение TRUE. В противном случае возвращается значение FALSE. Причину ошибки можно определить, обратившись к функции GetLastError.

Метод SetOption

Метод SetOption позволяет установить различные характеристики сеанса связи. Прототип метода SetOption практически полностью соответствует прототипу метода QueryOption:

BOOL 
SetOption( 
   DWORD dwOption, 
   LPVOID lpBuffer, 
   DWORD dwBufferLength 
);
  
BOOL 
SetOption( 
   DWORD dwOption, 
   DWORD dwValue 
);

Метод Close

Одно приложение может создать несколько объектов класса CInternetSession. Объект CInternetSession должен существовать на протяжении всего сеанса связи с Internet. Когда вам понадобится завершить работу с Internet, вызовите для объекта CInternetSession виртуальный метод Close, определенный в классе CInternetSession:

virtual void Close();

Следует отметить, что деструктор класса CInternetSession сам вызывает метод Close.

Класс CInternetException

Для обработки ошибок, возникающих при работе с WinInet, в состав библиотеки MFC включен новый класс CInternetException. Точно также как и другие классы MFC, предназначенные для обработки различных исключительных ситуаций, класс CInternetException наследован от базового класса CException:

CObject -> CException -> CInternetException

Элементы данных класса 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 не завершена из за разрыва сеанса связи

Обработка исключения 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. Описание этого метода вы не обнаружите в справочной системе, что однако не мешает использовать его в своих приложениях. Если вы не удалите исключение, то по завершении приложения отладчик сообщит вам о наличии неосвобожденных областей памяти.

Как вызвать исключение 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...".

Класс 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.

Оператор HINTERNET

Как мы упоминали, конструктор класса CInternetSession вызывает функцию InternetOpen из программного интерфейса WinInet чтобы выполнить инициализацию библиотеки WinInet и создать новый сеанс связи с Internet. Функция InternetOpen возвращает идентификатор типа HINTERNET, который используется при последующих вызовах функций WinInet.

Когда вы используете библиотеку MFC, вы можете определить идентификатор сеанса, воспользовавшись оператором HINTERNET, который входит в состав класса CInternetSession:

operator HINTERNET() const;

Класс 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.

Работа со структурой каталогов сервера FTP

Среди методов класса CFtpConnection можно выделить группу методов, ориентированных на работу со структурой каталогов сервера FTP. В следующей таблице мы представили краткие описания этих методов:

Метод Описание
SetCurrentDirectory Позволяет перейти в заданный каталог на сервере FTP
GetCurrentDirectory Определяет имя текущего каталога на сервере FTP. Смотри также метод GetCurrentDirectoryAsURL
GetCurrentDirectoryAsURL Определяет адрес URL текущего каталога на сервере FTP
RemoveDirectory Удаляет определенный каталог с сервера FTP
CreateDirectory Создает на сервере FTP новый каталог

Рассмотрим для примера методы GetCurrentDirectory и GetCurrentDirectoryAsURL. Другие методы, предназначенные для работы со структурой каталогов сервера FTP, вы можете изучить самостоятельно.

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

Работа с файлами

Другая группа методов класса CFtpConnection предназначена для работы с файлами:

Метод Описание
Rename Переименовывает файл, находящийся на сервере FTP
Remove Удаляет файл с сервера FTP
PutFile Записывает файл на сервер
GetFile Позволяет загрузить определенный файл с сервера FTP
OpenFile Открывает файл, расположенный на сервере FTP для последующего чтения или записи

Еще один метод, который входит в состав класса CFtpConnection - Close. Этот метод закрывает соединение с сервером FTP, для чего он вызывает метод Close класса CInternetConnection. Деструктор класса CFtpConnection закрывает соединение с сервером даже если вы не вызвали метод Close.

Мы рассмотрим только три метода для работы с файлами. Это методы GetFile, PutFile и OpenFile. Описание остальных методов вы найдете в справочной системе Microsoft Visual C++.

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

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

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

Класс CHttpConnection

Класс CHttpConnection позволяет приложению взаимодействовать с сервером WWW. Этот класс наследуется от базового класса CInternetConnection:

CObject -> CInternetConnection -> CHttpConnection

Кроме конструктора в состав класса CHttpConnection входит только один метод OpenRequest, который устанавливает соединение с сервером HTTP.

Конструктор класса CHttpConnection никогда не вызывается напрямую. Объект класса CHttpConnection создается при вызове метода GetHttpConnection класса CInternetSession. Поэтому мы опустим описание конструктора класса и сразу перейдем к методу OpenRequest.

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

Во втором варианте метода 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, соответствующий запрашиваемому объекту.

Класс 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, содержащий идентификатор файла.

Методы SetWriteBufferSize и SetReadBufferSize

Программный интерфейс WinInet не выполняет буферизации при обмене данными с серверами Internet. Однако если вы используете классы MFC, то можете выполнять буферизацию данных если работаете с методами класса CInternetFile и методами классов, наследуемых от него.

Размер буфера для операций чтения и записи устанавливается отдельно. Для этого в состав класса CInternetFile включены методы SetWriteBufferSize и SetReadBufferSize, которые, соответственно, устанавливают размер буфера, для записи и для чтения данных с сервера:

BOOL SetWriteBufferSize( UINT nWriteSize );
BOOL SetReadBufferSize( UINT nReadSize );

Параметры nWriteSize и nReadSize устанавливают размер буфера в байтах.

В случае успешного завершения методы SetWriteBufferSize и SetReadBufferSize возвращают ненулевое значение. Если методы завершились с ошибкой, то они возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.

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

Метод WriteString

Антипод метода ReadString - метод WriteString записывает указанную ему строку данных в файл на сервере. Указатель на эту строку вы должны передать методу WriteString через параметр pstr:

virtual void WriteString( LPCTSTR pstr );
throw CInternetException( );

Если во время передачи данных возникнут какие-либо проблемы, метод WriteString может вызвать исключение CInternetException.

Метод Close

По окончании работы с файлом на сервере, его следует закрыть. Для этого вы можете воспользоваться методом Close:

virtual void Close();
throw( CInternetException );

В случае ошибки, метод Close может вызвать исключение CInternetException. Поэтому вы должны учесть такую возможность и поместить вызов этого метода внутри блока try.

Класс 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 и освобождает все используемые им ресурсы

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

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

Метод 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 Сервер перегружен

Метод GetFileURL

Метод GetFileURL возвращает имя файла в формате URL. Вы можете использовать данный метод после передачи запроса серверу с помощью метода SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.

Прототип метода GetFileURL представлен ниже:

virtual CString GetFileURL( ) const;

Метод Close

Виртуальный метод Close закрывает объект CHttpFile и освобождает все используемые им ресурсы. Вы можете использовать данный метод после передачи запроса серверу с методом SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.

Прототип метода Close представлен ниже:

virtual void Close( );

Метод Close не имеет параметров и не возвращает значения.

Класс 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

Метод GetLength

После того, как вы нашли файл при помощи метода FindNextFile, вы можете определить его длину. Для этого следует воспользоваться методом GetLength, который возвращает длину файла в байтах:

DWORD GetLength( ) const;

Метод GetFileURL

Обнаружив файл при помощи метода FindNextFile, вы можете определить его адрес в формате URL, вызвав виртуальный метод GetFileURL:

virtual CString GetFileURL( ) const;

Метод GetFileURL возвращает адрес файла в строке класса CString.

Метод GetLastWriteTime

В состав класса CFileFind входят несколько методов, которые определяют различные временные характеристики файла. Вы можете узнать время создания файла, время последнего обращения к файлу и время последнего изменения файла. Для этих целей предназначены методы GetCreationTime, GetLastAccessTime и GetLastWriteTime. Они имеют сходный формат, поэтому мы рассмотрим только метод GetLastWriteTime, остальные методы вы изучите самостоятельно.

Определены два варианта метода GetLastWriteTime, которые используют для своей работы либо традиционную структуру FILETIME, либо более соответствующий концепции библиотеки MFC объект класса CTime:

virtual BOOL GetLastWriteTime( FILETIME* pFileTime ) const;
virtual BOOL GetLastWriteTime( CTime& refTime ) const;

В случае успешного завершения метод GetLastWriteTime возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль.

Метод IsDirectory

Метод FindNextFile позволяет обнаружить не только файлы, но также и каталоги. Метод IsDirectory определяет какой объект получен методом FindNextFile - файл или каталог:

BOOL IsDirectory( ) const;

Если объект является каталогом, тогда метод IsDirectory возвращает значение TRUE, а если файлом - тогда FALSE.

Метод Close

Когда вы завершите поиск файлов, следует вызвать метод Close. Он завершает поиск и освобождает все задействованные ресурсы. После того как вы вызвали метод Close, вы можете повторить поиск, используя тот же самый объект CFileFind:

void Close( );

Класс CFtpFileFind

Класс CFtpFileFind наследуется от базового класса CFileFind и позволяет провести поиск файлов на сервере FTP.

Для нас будут представлять интерес конструктор класса CFtpFileFind и все остальные три метода, входящие в состав этого класса. Название этих методов мы привели в следующей таблице:

Метод Описание
CFtpFileFind Конструктор класса CFtpFileFind
FindFile Начинает поиск файла или файлов на сервере FTP
FindNextFile Продолжает поиск файла или файлов в указанном каталоге сервера FTP. Вы должны использовать этот метод после обращения к методу FindFile
GetFileURL Определяет адрес файла в формате URL

Если вы решите заняться разработкой приложения, взаимодействующего с серверами FTP, вы будете активно использовать все методы данного класса. Поэтому мы приведем их описания.

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

Конструктор класса CFtpFileFind имеет только один обязательный параметр pConnection. В нем вы должны указать объект класса CFtpConnection, представляющий соединение с сервером FTP, в рамках которого вы будете осуществлять поиск файлов или каталогов:

CFtpFileFind( 
   CFtpConnection* pConnection, 
   DWORD dwContext = 1 
);

В качестве второго параметра вы можете указать идентификатор контекста, который будет соответствовать данному объекту класса CFtpFileFind.

Метод FindFile

Чтобы приступить к поиску файла или каталога с заданным имененм надо воспользоваться методом FindFile. Он также позволяет приступить к поиску файлов и каталогов по шаблону имени. Прототип метода FindFile представлен ниже:

virtual BOOL FindFile( 
   LPCTSTR pstrName = NULL, 
   DWORD dwFlags = INTERNET_FLAG_RELOAD 
);

Имя файла или каталога, который вы желаете найти или соответствующий этому имени шаблон, вы должны записать в строку и передать указатель на нее методу FindFile через параметр pstrName. Чтобы выполнить поиск всех объектов - и файлов и каталогов, расположенных в текущем каталоге сервера FTP, вы можете указать в качестве шаблона для поиска символ звездочки * или просто использовать в качестве параметра pstrName значение NULL. Такой же эффект получается если вы просто опустите параметры метода и для них будут использованы значения, принятые по умолчанию.

Параметр dwFlags позволяет управлять процессом поиска. Вы, например, можете указать методу FindFile, что поиск следует осуществлять не используя данные которые записаны в кэш. Вот список флагов, которые можно использовать:

Их описание мы приводили, когда рассказывали о методе OpenRequest класса CHttpConnection.

В случае успешного завершения метод AddRequestHeaders возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.

Метод FindNextFile

После успешно завершившегося вызова метода FindFile вы должны вызвать виртуальный метод FindNextFile один или несколько раз для поиска всех файлов, соответствующих вашим требованиям. Только после того как вы хотя бы один раз вызвали метод FindNextFile вы можете воспользоваться другими методами класса CFindFile, чтобы определить характеристики обнаруженных файлов:

virtual BOOL FindNextFile( );

Метод FindNextFile возвращает ненулевое значение, если вы можете продолжить поиск и снова вызвать метод FindNextFile. Если метод FindNextFile вернул нулевое значение, значит был обнаружен последний файл соответствующий указанному вами шаблону поиска. В этом случае вы можете определить его характеристики, воспользовавшись методами класса FindFile и должны закончить поиск.

Метод GetFileURL

Если в качестве имени файла для поиска вы указали шаблон, то чтобы узнать имя обнаруженного объекта - файла или каталога вы можете воспользоваться методом GetFileURL класса CFtpFileFind:

CString GetFileURL( ) const;

Он возвращает адрес обнаруженного объекта в формате URL. Для определения имени объекта вы также можете воспользоваться некоторыми методами базового класса CFileFind.

Класс 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.

Класс CGopherLocator

Класс CGopherLocator наследуется непосредственно от базового класса CObject. Этот класс предназначен для доступа к серверам Gopher.

CObject -> CGopherLocator

В состав метода CGopherLocator входит конструктор класса, метод GetLocatorType и оператор LPCTSTR.

Метод GetLocatorType разбирает локатор (gopher locator) и определяет его атрибуты, а оператор LPCTSTR позволяет получить прямой доступ к данным, записанным в объекте CGopherLocator как к обычной строке символов.

Класс 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 Определяет длину файла в байтах

Функции 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.

Адреса 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.

Функция 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 возвращает ненулевое значение в случае успешного завершения и нуль в случае ошибки.

Приложение Parse

В этом разделе мы представим вашему вниманию приложение Parse. Оно использует функцию AfxParseURL для разбора на составные части адресов URL, которые пользователь может вводить в диалоговой панели приложения (рис. 1.3).

Приложение Parse не выполняет соединение с Internet и вы можете его запустить на компьютере, не имеющем ни модема, ни сетевой карты, и не подключенном к сетям Internet и Intranet. Конечно, пользы от такого приложения не много, но мы предлагаем вам с ним поработать, чтобы лучше разобраться со структурой адресов URL.

Рис. 1.3. Приложение ParseURL

Чтобы упростить приложение, мы выбрали для него интерфейс на основе диалоговой панели. Запустите MFC AppWizard, выбрав из меню File строку New. На экране появится одноименная диалоговая панель. Выберите в ней из списка New строку Project Workspace и нажмите кнопку OK. Откроется диалоговая панель New Project Workspace. В качестве типа создаваемого приложения укажите MFC AppWizard (exe) и введите в поле Name имя проекта - Parse. Затем нажмите кнопку Create.

На экране появится первая диалоговая панель MFC AppWizard - MFC AppWizard - Step 1. В ней вы должны выбрать тип пользовательского интерфейса приложения - Dialog based, то есть приложение с главной диалоговой панелью. В принципе, теперь вы можете нажать кнопку Finish и оставить все остальные характеристики приложения по умолчанию. Но чтобы упростить приложение и сделать его исходный текст более доступным для понимания, мы предлагаем вам сначала перейти к следующей панели MFC AppWizard Step 2 of 4 и отключить в ней переключатель About box. При этом MFC AppWizard не будет подключать к приложению информационную диалоговую панель About. За счет этого можно несколько упростить исходные тексты приложения.

После этого, нажмите кнопку Finish, просмотрите информацию о приложении, которая появится в диалоговой панели New Project Information, и нажмите на кнопку OK. MFC AppWizard создаст проект.

Мы уже детально рассматривали шаблон приложения с пользовательским интерфейсом на основе диалоговой панели в 28 томе серии “Библиотека системного программиста”. Поэтому мы сразу приступим к доработке шаблона приложения и не будем останавливаться на исходных текстах, созданных MFC AppWizard.

Сначала загрузите в редактор ресурсов шаблон главной диалоговой панели IDD_PARSEURL_DIALOG приложения Parse. Разместите на этой диалоговой панели пять полей редактирования IDC_EDIT_URL_ADDRESS, IDC_EDIT_SERVICE_TYPE, IDC_EDIT_SERVER_NAME, IDC_EDIT_OBJECT_NAME и IDC_EDIT_PORT_NUMBER. Все поля редактирования кроме IDC_EDIT_URL_ADDRESS сделайте доступными только для чтения. Для этого в панели свойств этих полей Edit Propeties на странице Styles установите переключатель Read-only. Около каждого поля редактирования сделайте соответствующие текстовые подписи URL Address, Service type, Server name, Object name и Port number.

По умолчанию MFC AppWizard создает в диалоговой панели IDD_PARSEURL_DIALOG две кнопки - кнопку OK с идентификатором IDOK и кнопку Cancel с идентификатором IDCANCEL. Кнопка Cancel нам не понадобится вы можете ее удалить. Вместо нее добавьте кнопку Convert, присвоив ей идентификатор IDC_BUTTON_CONVERT.

В листинге 1.1 мы привели фрагмент файла ресурсов приложения Parse, в котором определяется шаблон диалоговой панели IDD_PARSEURL_DIALOG. Ориентируйтесь на него, когда будете создавать эту диалоговую панель.

Листинг 1.1. Фрагмент файла Parse.rc, шаблон диалоговой панели IDD_PARSEURL_DIALOG

//////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_PARSEURL_DIALOG DIALOGEX 0, 0, 241, 210
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
                      WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "ParseURL"
FONT 8, "MS Sans Serif"
BEGIN
  DEFPUSHBUTTON   "OK",IDOK,184,183,50,14

  EDITTEXT  IDC_EDIT_URL_ADDRESS,7,27,227,15, ES_AUTOHSCROLL
  LTEXT     "URL Address",IDC_STATIC,7,15,43,8
  EDITTEXT  IDC_EDIT_SERVICE_TYPE,7,73,227,15,ES_AUTOHSCROLL | 
                                              ES_READONLY
  LTEXT     "Service type",IDC_STATIC,7,61,40,8
  EDITTEXT  IDC_EDIT_SERVER_NAME,7,110,227,15,ES_AUTOHSCROLL | 
                                              ES_READONLY
  LTEXT     "Server name",IDC_STATIC,7,97,41,8
  EDITTEXT  IDC_EDIT_OBJECT_NAME,7,147,227,15,ES_AUTOHSCROLL | 
                                              ES_READONLY
  LTEXT     "Object name",IDC_STATIC,7,135,41,8
  EDITTEXT  IDC_EDIT_PORT_NUMBER,7,182,52,15, ES_AUTOHSCROLL | 
                                              ES_READONLY
  LTEXT     "Port number",IDC_STATIC,7,170,39,8

  PUSHBUTTON      "Convert",IDC_BUTTON_CONVERT,115,183,50,14
END

Все идентификаторы, которые создаются автоматически во время редактирования ресурсов приложения, в том числе диалоговой панели IDD_PARSEURL_DIALOG, записываются в файле resource.h. Исходный текст этого файла мы привели в листинге 1.2.

Листинг 1.2. Файл resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by ParseURL.rc
//
#define IDM_ABOUTBOX                    0x0010
#define IDD_ABOUTBOX                    100
#define IDS_ABOUTBOX                    101
#define IDD_PARSEURL_DIALOG             102
#define IDR_MAINFRAME                   128
#define IDC_EDIT_URL_ADDRESS            1000
#define IDC_EDIT_SERVICE_TYPE           1001
#define IDC_EDIT_SERVER_NAME            1002
#define IDC_EDIT_OBJECT_NAME            1003
#define IDC_EDIT_PORT_NUMBER            1004
#define IDC_BUTTON_CONVERT              1005

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        129
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1006
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

После того, как вы завершите подготовку диалоговой панели IDD_PARSEURL_DIALOG, запустите MFC ClassWizard. С его помощью мы привяжем к полям редактирования диалоговой панели новые элементы данных класса CParseURLDlg.

В диалоговой панели MFC ClassWizard перейдите на страницу Member Variables. Выберите в поле Class name имя класса CParseURLDlg и добавьте к нему с помощью кнопки Add Variable пять переменных, привязав их к полям ввода. К полю IDC_EDIT_URL_ADDRESS привяжите переменную m_UrlAddress, к IDC_EDIT_SERVICE_TYPE - m_ServiceType, IDC_EDIT_SERVER_NAME - m_ServerName и к IDC_EDIT_PORT_NUMBER - m_PortNumber. В качестве типа переменных выберите для всех них класс CString. Тогда используя механизм DDX вы легко сможете обмениваться данными между полями редактирования и соответствующими строками.

В исходных текстах приложения MFC ClassWizard модифицирует конструктор и метод DoDataExchange класса CParseURLDlg. В конструкторе класса CParseURLDlg добавляется программный код, выполняющий инициализацию строк, привязанных к полям редактирования диалоговой панели. Как видно из листинга 1.3, соответствующие команды добавляются внутри блока AFX_DATA_INIT.

Листинг 1.3. Фрагмент файла Parse.cpp, конструктор класса CParseURLDlg

CParseURLDlg::CParseURLDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CParseURLDlg::IDD, pParent)
{
   //{{AFX_DATA_INIT(CParseURLDlg)
   m_ObjectName = _T("");
   m_PortNumber = _T("");
   m_ServerName = _T("");
   m_ServiceType = _T("");
   m_UrlAddress = _T("");
   //}}AFX_DATA_INIT

   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

К методу DoDataExchange класса CParseURLDlg MFC ClassWizard добавляет методы DDX_Text, фактически осуществляющие обмен данными между полями редактирования диалоговой панели и соответствующими текстовыми строками. Мы привели фрагмент файла Parse.cpp с определением метода DoDataExchange в листинге 1.4.

Листинг 1.4. Фрагмент файла Parse.cpp, метод DoDataExchange класса CParseURLDlg

void CParseURLDlg::DoDataExchange(CDataExchange* pDX)

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.

Как работает приложение 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);

После того, как все переменные, привязанные к полям редактирования диалоговой панели, заполнены, вызываем метод UpdateData с параметром FALSE. Он отображает их содержимое в диалоговой панели:

UpdateData(FALSE);