Программный интерфейс 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, которые интегрируют в себе все возможности этого программного интерфейса.
Библиотека 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
Приложения, взаимодействующие с серверами Internet посредством программного интерфейса WinInet и надстроенных над ним классов библиотеки MFC, должны придерживаться стандартных сценариев установления связи и обмена данными, которые мы рассмотрим в данном разделе.
Любое приложение, использующее классы и методы WinInet, должно, в первую очередь, инициализировать сеанс работы с интерфейсом WinInet или, другими словами, сеанс связи. Для этого следует создать объект класса CInternetSession. Одно приложение может установить несколько сеансов связи, если того требует выполняемая им задача.
Только после этого в рамках данного сеанса возможна дальнейшая работа с серверами Internet, будь то установление связи с серверами WWW или FTP, чтение и запись файлов, поиск файлов по заданному адресу и т. д. Единственное что вы можете делать с программным интерфейсом WinInet до того, как создан сеанс - это выполнять разбор адресов URL при помощи глобальной функции AfxParseURL. Описание этой функции и пример ее использования вы найдете в разделе “Адреса URL”.
По окончании работы следует закрыть сеанс связи и удалить соответствующий объект класса CInternetSession, если он был создан динамически.
Как только создан объект класса CInternetSession, представляющий сеанс связи, вы можете воспользоваться методами этого класса, чтобы изменить некоторые его характеристики. Так, например, вы можете установить ограничение на выполнение некоторых операций, которые могут отнять много времени. Для этого вы можете использовать методы SetOption и QueryOption. Заметим, однако, что в большинстве случаев изменять характеристики сеанса связи нет никакой необходимости и вы можете сразу приступить к выполнению основной задачи - соединиться с сервером WWW или FTP, просмотреть структуру его каталогов, прочитать или записать файл.
В случае возникновения ошибок, многие методы классов WinInet вызывают исключение CInternetException. Чтобы правильно обрабатывать эти исключения, вы должны поместить методы, которые могут вызвать исключение, в блок try и определить соответствующий блок catch, выполняющий его обработку.
У вас существует две принципиально разные возможности работы с серверами Internet. Первая предполагает, что вам надо только выполнить чтение объекта, адрес которого известен. Вторая возможность требует более сложных манипуляций, зато позволяет выполнять специфические операции, например поиск файлов на сервере FTP, создание и удаление каталогов, передача запросов на сервер WWW и т. д.
В этом случае вы можете сразу вызвать метод OpenURL класса CInternetSession. Он выполнит соединение с указанным ему сервером и создаст объект класса CInternetFile, соответствующий необходимому вам файлу.
Далее вы можете воспользоваться методами класса CInternetFile, чтобы прочитать данный файл с сервера на локальный компьютер. Для этой цели вы можете вызвать метод Read, который читает файл и записывает считанные данные в буфер, или метод ReadString, который считывает из файла текстовую строку, ограниченную символом перевода строки.
Описанный метод получения файла не позволяет выполнить обратную задачу - записать файл на сервер FTP. Серверы WWW и Gopher такую операцию не допускают в любом случае. От них вы можете только получать информацию.
Чтобы выполнять с серверами Internet специфические операции, например создать на сервере FTP новый каталог или передать запрос на сервер WWW, вы должны сначала установить соединение с сервером данного типа. Для этого в состав класса CInternetSession включены три специальные метода - GetFtpConnection, GetHttpConnection и GetGopherConnection. Они, соответственно, открывают соединение с серверами FTP, WWW и Gopher, а также создают объект представляющий соединение с сервером соответствующего типа.
Так метод GetFtpConnection, который выполняет соединение с сервером FTP, возвращает объект класса CFtpConnection, метод GetHttpConnection - объект класса СHttpConnection, а метод GetGopherConnection - объект класса СGopherConnection. Далее вы можете обращаться к методам этих классов и выполнять различные операции с сервером.
К сожалению, классы 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 перед использованием остальных классов и методов 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( 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 выполняет различные действия для разных типов 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 выполняет соединение с заданным сервером 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 имеет сходное название с методом 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, которое вы должны обрабатывать в своем приложении.
Чтобы разрешить программному интерфейсу WinInet вызывать метод OnStatusCallback, необходимо воспользоваться методом EnableStatusCallback, прототип которого мы представили ниже:
BOOL EnableStatusCallback( BOOL bEnable = TRUE ); throw ( CInternetException );
Метод EnableStatusCallback имеет только один параметр, который разрешает или запрещает использование метода OnStatusCallback. По умолчанию параметр bEnable содержит значение TRUE, которое разрешает его использование.
Метод EnableStatusCallback возвращает ненулевое значение в случае успешного завершения или ноль в случае ошибки. Данный метод может вызывать исключение CInternetException. Вы можете определить причину исключения, воспользовавшись методами класса CInternetException.
Виртуальный метод 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:
Определяет IP-адрес имени, записанного в параметре lpvStatusInformation.
В буфере lpvStatusInformation находится IP-адрес сервера, имя которого вы указали.
Выполняется соединение по адресу сокета, указанного в буфере lpvStatusInformation.
Установлено соединение по адресу сокета, указанного в буфере lpvStatusInformation.
Передается информационный запрос на сервер. Параметр lpvStatusInformation не используется.
Информационный запрос передан на сервер. Параметр lpvStatusInformation не используется.
Ожидает ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.
Получен ответ на запрос, переданный серверу. Параметр lpvStatusInformation не используется.
Соединение с сервером закрывается. Параметр lpvStatusInformation не используется.
Соединение с сервером закрыто. Параметр lpvStatusInformation не используется.
Функция InternetConnect из программного интерфейса WinInet создала новый идентификатор.
Данный идентификатор удален.
Асинхронная операция завершена. Параметр 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, позволяет определить различные характеристики сеанса связи. В состав класса 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, описаны ниже.
Адрес функции обратного вызова, установленной для данного сеанса.
Время, отведенное на установление соединения. Задается в миллисекундах. Если соединение не устанавливается в течении данного времени, оно отменяется. По умолчанию время на соединение никак не ограничено.
Количество попыток соединения. В случае неудачной попытки соединения выполняется соответствующее количество повторных попыток. По умолчанию выполняется 5 попыток.
Задержка в миллисекундах между повторными попытками соединения.
Время, отведенное для передачи запроса, не связанного с передачей данных. Задается в миллисекундах. По умолчанию время на передачу запроса не ограничено
Время, отведенное для приема запроса, не связанного с получением данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.
Время, отведенное для передачи запроса с данными. Задается в миллисекундах. По умолчанию время на передачу запроса не ограничено.
Время, отведенное для запроса на прием данных. Задается в миллисекундах. По умолчанию время на прием запроса не ограничено.
Тип идентификатора Internet, например INTERNET_HANDLE_TYPE_CONNECT_FTP или INTERNET_HANDLE_TYPE_HTTP_REQUEST.
Идентификатор контекста, связанный с данным идентификатором Internet.
Размер буфера, используемого для чтения данных с сервера FTP.
Размер буфера, используемого для передачи данных на сервер FTP.
Идентификатор последнего асинхронного запроса, который выполнен в данной задаче.
Приоритет асинхронной операции загрузки данных.
Родительский идентификатор.
Определяет, используется ли данным идентификатором уже существующее соединение.
Имя пользователя, связанное с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.
Пароль, связанный с идентификатором, который возвращает функция InternetConnect из программного интерфейса WinInet.
Информация о текущем процессе загрузки. Если загрузка идет из кэш-памяти, то возвращается флаг INTERNET_REQFLAG_FROM_CACHE.
Код ошибки программного интерфейса Winsock, который вызвал ошибку ERROR_INTERNET_ в данной задаче приложения.
Если метод QueryOption завершился без ошибок, он возвращает значение TRUE. В противном случае возвращается значение FALSE. Причину ошибки можно определить, обратившись к функции GetLastError.
Метод SetOption позволяет установить различные характеристики сеанса связи. Прототип метода SetOption практически полностью соответствует прототипу метода QueryOption:
BOOL SetOption( DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength ); BOOL SetOption( DWORD dwOption, DWORD dwValue );
Одно приложение может создать несколько объектов класса CInternetSession. Объект CInternetSession должен существовать на протяжении всего сеанса связи с Internet. Когда вам понадобится завершить работу с Internet, вызовите для объекта CInternetSession виртуальный метод Close, определенный в классе CInternetSession:
virtual void Close();
Следует отметить, что деструктор класса CInternetSession сам вызывает метод Close.
Для обработки ошибок, возникающих при работе с WinInet, в состав библиотеки MFC включен новый класс CInternetException. Точно также как и другие классы MFC, предназначенные для обработки различных исключительных ситуаций, класс CInternetException наследован от базового класса CException:
CObject -> CException -> 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. Мы организуем обработку этих исключений с помощью блоков 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:
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 управляет наиболее общими характеристиками соединения с серверами 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.
Как мы упоминали, конструктор класса CInternetSession вызывает функцию InternetOpen из программного интерфейса WinInet чтобы выполнить инициализацию библиотеки WinInet и создать новый сеанс связи с Internet. Функция InternetOpen возвращает идентификатор типа HINTERNET, который используется при последующих вызовах функций WinInet.
Когда вы используете библиотеку MFC, вы можете определить идентификатор сеанса, воспользовавшись оператором HINTERNET, который входит в состав класса CInternetSession:
operator HINTERNET() const;
Класс 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.
Среди методов класса CFtpConnection можно выделить группу методов, ориентированных на работу со структурой каталогов сервера FTP. В следующей таблице мы представили краткие описания этих методов:
Метод |
Описание |
SetCurrentDirectory |
Позволяет перейти в заданный каталог на сервере FTP |
GetCurrentDirectory |
Определяет имя текущего каталога на сервере FTP. Смотри также метод GetCurrentDirectoryAsURL |
GetCurrentDirectoryAsURL |
Определяет адрес URL текущего каталога на сервере FTP |
RemoveDirectory |
Удаляет определенный каталог с сервера FTP |
CreateDirectory |
Создает на сервере FTP новый каталог |
Рассмотрим для примера методы GetCurrentDirectory и GetCurrentDirectoryAsURL. Другие методы, предназначенные для работы со структурой каталогов сервера FTP, вы можете изучить самостоятельно.
Метод 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 записывает длину строки с именем каталога.
В класс 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 копирует файл с сервера 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.
По сравнению с методом 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.
Если методы 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 позволяет приложению взаимодействовать с сервером WWW. Этот класс наследуется от базового класса CInternetConnection:
CObject -> CInternetConnection -> CHttpConnection
Кроме конструктора в состав класса CHttpConnection входит только один метод OpenRequest, который устанавливает соединение с сервером HTTP.
Конструктор класса CHttpConnection никогда не вызывается напрямую. Объект класса CHttpConnection создается при вызове метода GetHttpConnection класса CInternetSession. Поэтому мы опустим описание конструктора класса и сразу перейдем к методу 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, соответствующий запрашиваемому объекту.
В 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, содержащий идентификатор файла.
Программный интерфейс WinInet не выполняет буферизации при обмене данными с серверами Internet. Однако если вы используете классы MFC, то можете выполнять буферизацию данных если работаете с методами класса CInternetFile и методами классов, наследуемых от него.
Размер буфера для операций чтения и записи устанавливается отдельно. Для этого в состав класса CInternetFile включены методы SetWriteBufferSize и SetReadBufferSize, которые, соответственно, устанавливают размер буфера, для записи и для чтения данных с сервера:
BOOL SetWriteBufferSize( UINT nWriteSize ); BOOL SetReadBufferSize( UINT nReadSize );
Параметры nWriteSize и nReadSize устанавливают размер буфера в байтах.
В случае успешного завершения методы SetWriteBufferSize и SetReadBufferSize возвращают ненулевое значение. Если методы завершились с ошибкой, то они возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод 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.
Антипод метода ReadString - метод WriteString записывает указанную ему строку данных в файл на сервере. Указатель на эту строку вы должны передать методу WriteString через параметр pstr:
virtual void WriteString( LPCTSTR pstr ); throw CInternetException( );
Если во время передачи данных возникнут какие-либо проблемы, метод WriteString может вызвать исключение CInternetException.
По окончании работы с файлом на сервере, его следует закрыть. Для этого вы можете воспользоваться методом Close:
virtual void Close(); throw( CInternetException );
В случае ошибки, метод Close может вызвать исключение CInternetException. Поэтому вы должны учесть такую возможность и поместить вызов этого метода внутри блока try.
Класс 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 добавляет один или несколько заголовков к запросу 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_IF_NEW |
Добавляет новый заголовок только в том случае, если заголовок с таким именем отсутствует. В противном случае метод AddRequestHeaders завершается с ошибкой |
HTTP_ADDREQ_FLAG_ADD |
Добавляет заголовок только в том случае, если заголовок с таким именем отсутствует. Может использоваться совместно с флагом HTTP_ADDREQ_FLAG_REPLACE |
В случае успешного завершения метод AddRequestHeaders возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
Метод 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 позволяет узнать ответ сервера на переданный ему запрос и заголовки самого запроса. Вы можете использовать данный метод после передачи запроса серверу с помощью метода 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 позволяет определить код завершения данного запроса 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 возвращает имя файла в формате URL. Вы можете использовать данный метод после передачи запроса серверу с помощью метода SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.
Прототип метода GetFileURL представлен ниже:
virtual CString GetFileURL( ) const;
Виртуальный метод Close закрывает объект CHttpFile и освобождает все используемые им ресурсы. Вы можете использовать данный метод после передачи запроса серверу с методом SendRequest и или после того как вы открыли объект на сервере Internet с помощью метода OpenURL.
Прототип метода Close представлен ниже:
virtual void Close( );
Метод Close не имеет параметров и не возвращает значения.
Класс 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 |
После того, как вы нашли файл при помощи метода FindNextFile, вы можете определить его длину. Для этого следует воспользоваться методом GetLength, который возвращает длину файла в байтах:
DWORD GetLength( ) const;
Обнаружив файл при помощи метода FindNextFile, вы можете определить его адрес в формате URL, вызвав виртуальный метод GetFileURL:
virtual CString GetFileURL( ) const;
Метод GetFileURL возвращает адрес файла в строке класса CString.
В состав класса CFileFind входят несколько методов, которые определяют различные временные характеристики файла. Вы можете узнать время создания файла, время последнего обращения к файлу и время последнего изменения файла. Для этих целей предназначены методы GetCreationTime, GetLastAccessTime и GetLastWriteTime. Они имеют сходный формат, поэтому мы рассмотрим только метод GetLastWriteTime, остальные методы вы изучите самостоятельно.
Определены два варианта метода GetLastWriteTime, которые используют для своей работы либо традиционную структуру FILETIME, либо более соответствующий концепции библиотеки MFC объект класса CTime:
virtual BOOL GetLastWriteTime( FILETIME* pFileTime ) const; virtual BOOL GetLastWriteTime( CTime& refTime ) const;
В случае успешного завершения метод GetLastWriteTime возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль.
Метод FindNextFile позволяет обнаружить не только файлы, но также и каталоги. Метод IsDirectory определяет какой объект получен методом FindNextFile - файл или каталог:
BOOL IsDirectory( ) const;
Если объект является каталогом, тогда метод IsDirectory возвращает значение TRUE, а если файлом - тогда FALSE.
Когда вы завершите поиск файлов, следует вызвать метод Close. Он завершает поиск и освобождает все задействованные ресурсы. После того как вы вызвали метод Close, вы можете повторить поиск, используя тот же самый объект CFileFind:
void Close( );
Класс CFtpFileFind наследуется от базового класса CFileFind и позволяет провести поиск файлов на сервере FTP.
Для нас будут представлять интерес конструктор класса CFtpFileFind и все остальные три метода, входящие в состав этого класса. Название этих методов мы привели в следующей таблице:
Метод |
Описание |
CFtpFileFind |
Конструктор класса CFtpFileFind |
FindFile |
Начинает поиск файла или файлов на сервере FTP |
FindNextFile |
Продолжает поиск файла или файлов в указанном каталоге сервера FTP. Вы должны использовать этот метод после обращения к методу FindFile |
GetFileURL |
Определяет адрес файла в формате URL |
Если вы решите заняться разработкой приложения, взаимодействующего с серверами FTP, вы будете активно использовать все методы данного класса. Поэтому мы приведем их описания.
Конструктор класса CFtpFileFind имеет только один обязательный параметр pConnection. В нем вы должны указать объект класса CFtpConnection, представляющий соединение с сервером FTP, в рамках которого вы будете осуществлять поиск файлов или каталогов:
CFtpFileFind( CFtpConnection* pConnection, DWORD dwContext = 1 );
В качестве второго параметра вы можете указать идентификатор контекста, который будет соответствовать данному объекту класса CFtpFileFind.
Чтобы приступить к поиску файла или каталога с заданным имененм надо воспользоваться методом FindFile. Он также позволяет приступить к поиску файлов и каталогов по шаблону имени. Прототип метода FindFile представлен ниже:
virtual BOOL FindFile( LPCTSTR pstrName = NULL, DWORD dwFlags = INTERNET_FLAG_RELOAD );
Имя файла или каталога, который вы желаете найти или соответствующий этому имени шаблон, вы должны записать в строку и передать указатель на нее методу FindFile через параметр pstrName. Чтобы выполнить поиск всех объектов - и файлов и каталогов, расположенных в текущем каталоге сервера FTP, вы можете указать в качестве шаблона для поиска символ звездочки * или просто использовать в качестве параметра pstrName значение NULL. Такой же эффект получается если вы просто опустите параметры метода и для них будут использованы значения, принятые по умолчанию.
Параметр dwFlags позволяет управлять процессом поиска. Вы, например, можете указать методу FindFile, что поиск следует осуществлять не используя данные которые записаны в кэш. Вот список флагов, которые можно использовать:
Их описание мы приводили, когда рассказывали о методе OpenRequest класса CHttpConnection.
В случае успешного завершения метод AddRequestHeaders возвращает ненулевое значение. Если метод завершился с ошибкой, то он возвращает ноль, а причину ошибки можно узнать с помощью функции GetLastError.
После успешно завершившегося вызова метода FindFile вы должны вызвать виртуальный метод FindNextFile один или несколько раз для поиска всех файлов, соответствующих вашим требованиям. Только после того как вы хотя бы один раз вызвали метод FindNextFile вы можете воспользоваться другими методами класса CFindFile, чтобы определить характеристики обнаруженных файлов:
virtual BOOL FindNextFile( );
Метод FindNextFile возвращает ненулевое значение, если вы можете продолжить поиск и снова вызвать метод FindNextFile. Если метод FindNextFile вернул нулевое значение, значит был обнаружен последний файл соответствующий указанному вами шаблону поиска. В этом случае вы можете определить его характеристики, воспользовавшись методами класса FindFile и должны закончить поиск.
Если в качестве имени файла для поиска вы указали шаблон, то чтобы узнать имя обнаруженного объекта - файла или каталога вы можете воспользоваться методом GetFileURL класса CFtpFileFind:
CString GetFileURL( ) const;
Он возвращает адрес обнаруженного объекта в формате URL. Для определения имени объекта вы также можете воспользоваться некоторыми методами базового класса CFileFind.
Класс 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 наследуется непосредственно от базового класса CObject. Этот класс предназначен для доступа к серверам Gopher.
CObject -> CGopherLocator
В состав метода CGopherLocator входит конструктор класса, метод GetLocatorType и оператор LPCTSTR.
Метод GetLocatorType разбирает локатор (gopher locator) и определяет его атрибуты, а оператор LPCTSTR позволяет получить прямой доступ к данным, записанным в объекте CGopherLocator как к обычной строке символов.
Также как класс CHttpFile, класс CGopherFile наследуется от базового класса CInternetFile. Вместе с другими классами WinInet он позволяет организовать поиск и получение файлов с серверов Gopher.
В класс CGopherFile входит конструктор CGopherFile и метод Close, который закрывает соединение с сервером Gopher.
Класс CGopherFileFind наследуется от базового класса CFileFind и используется для поиска файлов на серверах Gopher. В следующей таблице мы привели описание различных методов класса CGopherFileFind:
Метод |
Описание |
CGopherFileFind |
Конструктор класса CGopherFileFind. Создает объект класса CGopherFileFind |
FindFile |
Начинает поиск файла или файлов на сервере Gopher |
FindNextFile |
Продолжает поиск файла или файлов в указанном каталоге сервера Gopher. Вы должны использовать этот метод после обращения к методу FindFile |
GetLocator |
Определяет объект CGopherLocator |
GetScreenName |
Определяет имя экрана Gopher |
GetLength |
Определяет длину файла в байтах |
Кроме классов 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.
Ресурсы, доступные через сеть 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 разбирает текстовую строку, содержащую универсальный указатель ресурсов 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. Оно использует функцию 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 ничем не отличается от других приложений с пользовательским интерфейсом на основе диалоговой панели, сформированных средствами 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);