Разработка приложений для Internet

         

Метод OnInitDialog


После того, как метод InitInstance главного класса приложения CFtpViewApp создает объект dlg класса CFtpViewDlg, представляющий диалоговую панель, для него вызывается метод DoModal:

int nResponse = dlg.DoModal();

Этот метод кроме прочего передает диалоговой панели сообщение WM_INITDIALOG. В ответ на сообщение WM_INITDIALOG вызывается метод OnInitDialog, объявленный как виртуальный метод класса CDialog. Обратите внимание, что таблица сообщений класса CFtpViewDlg не содержит макрокоманд для обработки сообщения WM_INITDIALOG. Метод OnInitDialog вызывается непосредственно MFC.

Первоначально метод OnInitDialog был переопределен MFC AppWizard во время создания приложения. Затем мы его изменили, добавив программный код для инициализации органов управления диалоговой панели и инициализации сеанса работы с WinInet. Рассмотрим метод OnInitDialog более подробно.

Сначала вызывается метод OnInitDialog базового класса CDialog. Он выполняет основную работу по инициализации диалоговой панели - загружает соответствующий шаблон диалоговой панели из ресурсов приложения и т. д.:

CDialog::OnInitDialog();

Затем два раза вызывается метод SetIcon, определенный в базовом классе CWnd. Этот метод устанавливает пиктограммы стандартного и уменьшенного размера, которые будут отображаться в случае минимизации диалоговой панели:

SetIcon(m_hIcon,TRUE);  // Пиктограмма стандартного размера

SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера

Первый параметр m_hIcon, передаваемый методам SetIcon, представляет собой индекс пиктограммы IDR_MAINFRAME. Эта пиктограмма загружается конструктором класса CFtpViewDlg, описанным выше.

Далее метод OnInitDialog выполняет инициализацию списка IDC_FTP_LIST, в котором будет отображаться содержимое каталогов сервера FTP.

Для временного использования объявляются несколько переменных - объект rectList класса CRect, структура lv_column типа LV_COLUMN и список строк szColHeader:

// Структура, для описания характеристик колонок списка

LV_COLUMN   lv_column;


// Переменная для определения размера списка

CRect       rectList;

// Названия для колонок списка

TCHAR szColHeader[COL_NUM][10] = {

         _T("Name"),



         _T("Length"),

         _T("Date"),

         _T("Time"),

};

В переменную rectList записывается размер области диалоговой панели, которую занимает список IDC_FTP_LIST. Для этого мы вызываем метод GetWindowRect объекта, управляющего списком. Размеры списка нам потребуются, когда мы будем определять ширину колонок, входящих в список:

m_FtpList.GetWindowRect(&rectList);

Затем мы заполняем поля структуры lv_column. Эта структура определяет характеристики колонки списка. Мы будем ее использовать при создании колонок.

Сначала заполняется поле mask. В него заносится комбинация нескольких констант, определяющих какие поля данной структуры будут иметь значение. Мы указали, что используются четыре поля структуры lv_column - fmt, iSubItem, pszText и cx:

lv_column.mask = LVCF_FMT |   // Используется поле fmt

             LVCF_SUBITEM |   // Используется поле iSubItem

                LVCF_TEXT |   // Используется поле pszText

               LVCF_WIDTH;    // Используется поле cx

Поле fmt задает выравнивание по левому краю колонки ее заголовка и текста элементов. Мы выбрали выравнивание по левому краю, записав в это поле значение LVCFMT_LEFT:

lv_column.fmt = LVCFMT_LEFT;

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

lv_column.cx = (rectList.Width() / COL_NUM) - 1;

Остальные поля структуры lv_column - iSubItem и pszText различаются для каждой колонки. Поэтому мы заполняем их отдельно. Поле iSubItem означает номер колонки списка, а поле pszText - текст его заголовка.

В следующем цикле заполняются последние два поля структуры и метод InsertColumn включает колонку в список. Номера колонок изменяются от 0 до 3, а названия полей берутся из списка строк szColHeader:

// Определяем характеристики колонок списка



for (int i = 0; i < COL_NUM; i++) 

{

   // Номер колонки

   lv_column.iSubItem = i;

   // Зааголовок колонки

   lv_column.pszText = szColHeader[i];

   // Добавляем колонку с заданными свойствами к списку

   m_FtpList.InsertColumn(i, &lv_column);

}

Новые колонки добавляются в список с помощью метода InsertColumn, определенного в классе CListCtrl. Этому методу передается номер колонки и заполненная структура lv_column, описывающая добавляемую колонку.

В списке сервера FTP могут фигурировать два типа объектов - каталоги и файлы. Для выделения их в списке им присваиваются различные пиктограммы.

Чтобы в списке можно было отмечать отдельные элементы при помощи пиктограмм, необходимо сформировать список изображений. Для этого в состав библиотеки MFC включен класс CImageList. Метод OnInitDialog создает объект этого класса, представляющий список из двух изображений размером 16 х 16 пикселов и включает в него пиктограммы с изображением каталога (пиктограмма с идентификатором IDI_DIRECTORY) и файла (пиктограмма с идентификатором IDI_FILE):

// Создаем список из двух изображений размера 16 х 16

m_ImageList = new CImageList();

m_ImageList -> Create(16, 16, TRUE, 2, 2);

// Добавляем в список две пиктограммы -

// IDI_DIRECTORY (изображение каталога) и

// IDI_FILE(изображение файла )

m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_DIRECTORY));

m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_FILE));

Полученный список изображений выбирается для использования в списке IDC_FTP_LIST с помощью метода SetImageList класса CListCtrl. Первый параметр метода SetImageList содержит указатель на список изображений, а второй определяет их размер. Константа LVSIL_SMALL означает, что данный список изображений будет использоваться при отображении пиктограмм маленького размера:

m_FtpList.SetImageList(m_ImageList, LVSIL_SMALL);

По завершении инициализации списка IDC_FTP_LIST, метод OnInitDialog выводит в поле редактирования IDC_FTP_ADDRESS, адрес сервера FTP. Адрес сервера FTP, который будет использоваться по умолчанию записывается в элемент данных m_FtpAddress класса CFtpViewDlg:



m_FtpAddress = "dials.ccas.ru"; // Сервер FTP “ДиалогНаука”

Для нашего примера мы использовали сервер FTP АО “ДиалогНаука”, который имеет адрес ftp://dials.ccas.ru. Вы можете заменить этот адрес на адрес любого другого сервера FTP по своему усмотрению.

Поле редактирования, предназначенное для ввода адреса сервера, мы связали со строкой m_FtpAddress. Поэтому чтобы вывести ее на экран вызывается метод UpdateData и в качестве параметра ему указывается значение FALSE:

UpdateData(FALSE);

Далее метод OnInitDialog приступает к инициализации сеанса связи с Internet (программного интерфейса WinInet). Создается новый объект класса CInternetSession. В качестве параметров конструктору класса CInternetSession указывается только имя приложения - строка FtpView. Вы можете указать здесь любую другую строку или не использовать этот параметр совсем. Указатель на созданный объект заносится в элемент данных m_InternetSession, принадлежащий классу CFtpViewDlg:

// Создаем сеанс связи с Internet, указываем в качестве

// имени программы-клиента название приложения FtpView

m_InternetSession = new CInternetSession("FtpView");

В случае возникновения ошибки при создании объекта класса CInternetSession мы отображаем на экране соответствующее сообщение и завершаем работу приложения:

if(!m_InternetSession)

{

   AfxMessageBox("New Session Error", MB_OK);

   OnOK();

}

Если объект класса CInternetSession создан, инициализируем указатель m_FtpConnection, также принадлежащий классу CFtpViewDlg:

m_FtpConnection = NULL;

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

Метод OnInitDialog возвращает значение TRUE. Это означает, что фокус ввода будет установлен на первый орган управления диалоговой панели (первый орган диалоговой панели можно выбрать в редакторе диалоговой панели, выбрав из меню Layout строку Tab Order).

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


Метод OnNavigate


После запуска приложения навигатор не задействован. Чтобы он начал работать, пользователь должен выбрать из списка адрес сервера, а затем нажать кнопку Navigate. Когда пользователь нажимает эту кнопку, вызывается метод OnNavigate класса CLookDlg.

В нем мы сначала определяем адрес, выбранный из списка. Для этого мы вызываем метод UpdateData с параметром TRUE. Он копирует строку выбранную из списка IDC_COMBO_ADDRESS в строку m_address:

UpdateData(TRUE);

Этот адрес передается навигатору, он соединяется с указанным сервером и отображает соответствующую страницу в своем окне:

m_explorer.Navigate(

      m_address,  

      NULL, NULL, NULL, NULL

);



Метод OnNavigateCompleteExplorer


После того, как навигатор завершит загрузку страницы WWW он вызывает метод OnNavigateCompleteExplorer и передает ему точный адрес этой страницы:

void CLookDlg::OnNavigateCompleteExplorer(LPCTSTR URL)

{

}

Мы записываем этот адрес в строку m_address и отображаем его в диалоговой панели, для чего вызываем метод UpdateData с параметром FALSE:

m_address =  URL;

UpdateData(FALSE);



Метод OnOnTop


Метод OnOnTop во многом похож на уже описанный нами метод OnDblclkFtpLis, но используется не для входа, а для выхода из каталогов. Если вы вошли в каталог и желаете выйти из него на верхний уровень, вы должны нажать кнопку On top. В ответ на сообщение от этой кнопки вызывается метод OnOnTop. Он блокирует список IDC_FTP_LIST, вызывая метод EnableWindow для объекта m_FtpList, управляющего этим списком:

// Блокируем список IDC_FTP_LIST

m_FtpList.EnableWindow(FALSE);

Затем с помощью метода ReverseFind класса CString мы ищем последнее вхождение символа / в строке sCurentDirectory, содержащей имя текущего каталога сервера, содержимое которого показывается на экране.

Если метод ReverseFind не обнаруживает в строке с путем каталога символов /, он возвращает значение -1. Это означает что мы уже находимся в корневом каталоге сервера:

int iNum = sCurentDirectory.ReverseFind('/');

if(iNum == -1)

{

   // Если символ / не обнаружен, значит мы находимся в

   // корневом каталоге

   AfxMessageBox("No top directory");

}

Если же символ / найден, то обращаясь к методу Left мы удаляем все символы, расположенные справа от него (включительно). Таким образом, в строке sCurentDirectory теперь будет записан путь каталога верхнего уровня.

Так как мы изменили текущий каталог, содержимое которого показывается на экране, вызываем метод DirectoryView. Он просмотрит имена каталогов и файлов, расположенных по новому пути, и заполнит список:

else

{

   // Удаляем из строки с именем текущего каталога названия

   // последнего каталога

   sCurentDirectory = sCurentDirectory.Left(iNum);

   // Меняем форму курсора (курсор “ожидание”)

   CWaitCursor wait;  

  

   // Отображаем содержимое каталога верхнего уровня

   DirectoryView();  

}

Перед тем как вызывать метод DirectoryView, мы создаем объект wait класса CWaitCursor. При этом автоматически изменится внешний вид курсора приложения. Как только метод DirectoryView закончит свою работу и управление выйдет из блока else, объект wait будет удален, а внешний вид курсора восстановится.

После этого снимаем блокировку со списка IDC_FTP_LIST и отображаем новый текущий путь в поле IDC_STATUS:

// Снимаем блокировку списка IDC_FTP_LIST

m_FtpList.EnableWindow(TRUE);

// Отображаем на диалоговой панели новый путь каталога

m_Status.SetWindowText(sCurentDirectory);



Метод OnProgressChangeExplorer


В ходе загрузки объектов с сервера, навигатор вызывает метод OnProgressChangeExplorer. В качестве параметров этому методу передается текущее состояние загрузки Progress и максимальное ProgressMax:

void CLookDlg::OnProgressChangeExplorer(long Progress, long ProgressMax)

{

   . . .

}

Мы проверяем значение параметров Progress и ProgressMax:

if(Progress <= 0 | ProgressMax <= 0)

   return;

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

m_Progress.SetPos( (int) (Progress * 100) / ProgressMax);



Метод OnSelchangeComboAddress


Метод OnSelchangeComboAddress вызывается когда пользователь выбирает из списка IDC_COMBO_ADDRESS адрес нового сервера. В нем мы сразу вызываем метод UpdateData с параметром TRUE чтобы записать выбранный адрес в строку m_address:

UpdateData(TRUE);

Затем нам остается только вызвать метод Navigate для навигатора IDC_EXPLORER, представленного элементом данных m_explorer. Мы указали только первый параметр этого метода, который определяет адрес для просмотра:

m_explorer.Navigate(

      m_address,   // Адрес URL для просмотра

      NULL, NULL, NULL, NULL

);

Метод Navigate соединяется с сервером, загружает с него указанную страницу и отображает ее в окне навигатора диалоговой панели приложения.



Метод OnStatusCallback


Виртуальный метод OnStatusCallback класса CInternetSession вызывается программным интерфейсом WinInet и информирует приложение о состоянии связи.

Если вы желаете использовать метод OnStatusCallback, вы должны наследовать от класса CInternetSession новый класс, в котором следует переназначить метод OnStatusCallback. Затем вы должны вызвать метод EnableStatusCallback и разрешить использование метода OnStatusCallback.

Прототип метода OnStatusCallback мы привели ниже:

virtual void

OnStatusCallback(

   DWORD dwContext,

   DWORD dwInternetStatus,

   LPVOID lpvStatusInformation,

   DWORD dwStatusInformationLength

);

Через параметр dwContext методу OnStatusCallback передается идентификатор контекста операции о состоянии которой информируется приложение. Параметр dwInternetStatus содержит код состояния. По нему вы можете определить причину вызова метода. Дополнительная информация передается в буфере, по адресу lpvStatusInformation. Размер этого буфера передается в параметре dwStatusInformationLength. Ниже представлены возможные значение параметра dwInternetStatus:



Метод OnStatusTextChangeExplorer


Метод OnStatusTextChangeExplorer вызывается навигатором, когда он переходит к выполнению новой операции или когда со страницы WWW загружен текст для строки состояния.

Текст, для отображения в строке состояния передается через единственный параметр метода - Text:

void CLookDlg::OnStatusTextChangeExplorer(LPCTSTR Text)

{

   . . .

}

Мы выводим этот текст в поле редактирования IDC_STATUS_TEXT, вызывая метод SetDlgItemText класса CWnd:

SetDlgItemText(IDC_STATUS_TEXT, Text);



Метод OnTitleChangeExplorer


Метод OnTitleChangeExplorer вызывается навигатором, когда он загружает заголовок страницы WWW. Текст заголовка передается через параметр Text данного метода:

void CLookDlg::OnTitleChangeExplorer(LPCTSTR Text)

{

   . . .

}

Мы отображаем заголовок в поле редактирования IDC_TITLE_BAR диалоговой панели приложения. Для этого вызываем метод SetDlgItemText класса CWnd и передаем ему идентификатор поля редактирования и текст заголовка:

SetDlgItemText(IDC_TITLE_BAR, Text);



Метод OpenFile


Если методы GetFile и PutFile вас не устраивают, потому что вы не получаете в этом случае достаточный контроль за передачей и приемом файла или если надо выполнить обмен данными между файлами на сервере FTP и оперативной памятью локального компьютера, то вы можете сначала открыть файл при помощи метода OpenFile, а затем считывать из него или записывать в него данные непосредственно из буфера на локальном компьютере при помощи методов Read и Write класса CInternetFile.

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

Приведем прототип метода OpenFile:

CInternetFile* OpenFile(

   LPCTSTR pstrFileName,

   DWORD dwAccess = GENERIC_READ,

   DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY,

   DWORD dwContext = 1

);

Имя файла, который надо открыть, передается методу OpenFile через параметр pstrFileName.

Режим, в котором открывается файл, задается параметром dwAccess. Если файл открывается для чтения, то в качестве параметра dwAccess используется значение GENERIC_READ, а если для записи - GENERIC_WRITE.

Параметр dwFlags определяет метод передачи файла, а параметр dwContext - идентификатор контекста. Назначение этих параметров полностью соответствует соответствующим параметрам методов GetFile и PutFile.

После того, как вы открыли файл на сервере, вы можете обращаться только к методам Read, Write и Close класса CInternetFile, а также метод FindFile класса CFtpFileFind. До тех пор, пока файл не будет закрыт, вы не сможете вызвать другие методы для данного сеанса связи с сервером FTP. В противном случае они будут завершаться с ошибкой, имеющий код FTP_ETRANSFER_IN_PROGRESS.



Метод OpenRequest


Метод OpenRequest создает запрос для передачи его серверу WWW. После того, как запрос создан, его можно модифицировать при помощи метода AddRequestHeaders класса CHttpFile, а затем передать серверу, вызвав метод SendRequest, входящий в состав класса CHttpFile.

Определены два варианта метода OpenRequest. Ниже мы привели для вас их прототипы:

CHttpFile* OpenRequest(

   LPCTSTR pstrVerb,

   LPCTSTR pstrObjectName,

   LPCTSTR pstrReferer = NULL,

   DWORD dwContext = 1,

   LPCTSTR* pstrAcceptTypes = NULL,

   LPCTSTR pstrVersion = NULL,

   DWORD dwFlags = INTERNET_FLAG_EXISTING_CONNECT

);

 

CHttpFile* OpenRequest(

   int nVerb,

   LPCTSTR pstrObjectName,

   LPCTSTR pstrReferer = NULL,

   DWORD dwContext = 1,

   LPCTSTR* pstrAcceptTypes = NULL,

   LPCTSTR pstrVersion = NULL, 

   DWORD dwFlags = INTERNET_FLAG_EXISTING_CONNECT

);

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

В первом варианте метода OpenRequest параметр pstrVerb должен содержать указатель на строку с командой в текстовом виде. Если использовать в качестве pstrVerb значение NULL, то по умолчанию передается команда "GET". Вы можете использовать команды, предоставленные в следующей таблице:

Команды

Описание

POST

Передать запрос на сервер, содержащий дополнительные данные. Это может быть, например, обращение к расширению CGI или ISAPI в котором ему передаются данные из формы, заполненной пользователем

GET

Запрос на получение объекта с сервера WWW

HEAD

Запрос на получение информации об объекте с сервера WWW. Сам объект не принимается

PUT

Записать данный объект на сервер по определенному адресу URL

LINK

Устанавливает связь между ресурсом, идентифцируемом адресом URL и другими ресурсами

DELETE

Предлагает серверу удалить ресурс по данному адресу URL

UNLINK

Удалляет связи ресурса с указанным адресом URL

¨     Отметим, что обычно используются только команды GET, HEAD и PUT


Во втором варианте метода OpenRequest параметр nVerb имеет целочисленный тип. В этом случае для задания команды вы должны использовать константы, представленные в следующей таблице.

Команда

Константа

POST

HTTP_VERB_POST

GET

HTTP_VERB_GET

HEAD

HTTP_VERB_HEAD

PUT

HTTP_VERB_PUT

LINK

HTTP_VERB_LINK

DELETE

HTTP_VERB_DELETE

UNLINK

HTTP_VERB_UNLINK

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

class CHttpConnection : public CInternetConnection

{

public:

   enum {

      _HTTP_VERB_MIN      = 0,

      HTTP_VERB_POST      = 0,

      HTTP_VERB_GET       = 1,

      HTTP_VERB_HEAD      = 2,

      HTTP_VERB_PUT       = 3,

      HTTP_VERB_LINK      = 4,

      HTTP_VERB_DELETE    = 5,

      HTTP_VERB_UNLINK    = 6,

      _HTTP_VERB_MAX      = 6,

   };

. . .

Объект, на который нацелена данная команда передается методу OpenRequest через параметр pstrObjectName. Он должен содержать указатель на строку с именем объекта, например именем файла, исполняемого модуля CGI, расширения ISAPI и т. д.

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

Так, через параметр pstrReferer можно передать адрес URL объекта, из которого вы получили адрес запрашиваемого объекта. По умолчанию этот параметр равен NULL и соответствующий адрес не используется.

Параметр dwContext задает идентификатор контекста для данной операции запроса. Более подробно идентификатор контекста рассматривается в описании метода GetFile класса CFtpConnection.

С помощью параметра pstrAcceptTypes ваше приложение может сообщить серверу тип информации, который оно может воспринять. Например, приложение может принимать и обрабатывать только текстовый файлы, файлы изображений, и т. д.

Если вы не укажете параметр pstrAcceptTypes или запишете в него значение NULL, то сервер будет считать, что приложение способно работать только с текстовыми файлами типа "text/*".



Переменная pstrVersion содержит указатель на строку, в которой определяется версия HTTP, используемая вашим приложением. Если вы опустите этот параметр, то будет использоваться версия HTTP/1.0.

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

Флаг

Описание

INTERNET_FLAG_RELOAD

Указывает на необходимость загрузки запрашиваемого объекта или информации непосредственно с сервера Internet, даже в том случае если данная информация уже содержится в кэше

INTERNET_FLAG_DONT_CACHE

Предупреждает запись полученной информации к кэш

INTERNET_FLAG_MAKE_PERSISTENT

Записывает полученную с сервера информацию в кэш и запрещает ее удаление во время операции очистки кэша

INTERNET_FLAG_SECURE

Использует семантику защищенных транзакций (secure transaction semantics)

INTERNET_FLAG_NO_AUTO_REDIRECT

Отменяет автоматическое перенаправление запросов методом SendRequest класса CHttpFile. Используется только в запросах по протоколу HTTP

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


Метод OpenURL


Метод OpenURL выполняет различные действия для разных типов URL. Вы можете использовать метод OpenURL для доступа к серверу FTP:

CStdioFile* OpenURL(

   LPCTSTR pstrURL,

   DWORD dwContext = 1,

   DWORD dwFlags = 0,

   LPCTSTR pstrHeaders = NULL,

   DWORD dwHeadersLength = 0

);

throw ( CInternetException );

Первый параметр pstrURL является обязательным. Через него вы должны передать указатель на строку, содержащую адрес URL объекта. Метод OpenURL предназначен для получения доступа к серверам FTP, WWW, системе Gopher и файлам на локальном компьютере. Поэтому в качестве схемы доступа URL могут быть указаны только ftp:, gopher:, http: и file:.

Метод OpenURL возвращает указатель на объект класса CStdioFile, соответствующий заданному адресу URL. Заметим, что этот указатель может указывать на объекты различных классов, для которых класс CStdioFile является базовым. Класс объекта на который возвращает указатель метод OpenURL, зависит от типа URL.

Метод OpenURL выполняет разбор полученной им строки URL. Для этого используется уже описанный нами метод AfxParseURL (раздел “Глобальная функция AfxParseURL”).

Если метод OpenURL определяет, что в качестве параметра pstrURL вы указали URL файла на локальном компьютере, то он создает новый объект класса CStdioFile и возвращает указатель на него.

При создании объект класса CStdioFile указываются атрибуты CFile::modeRead и CFile::shareCompat. Поэтому файл открывается только для чтения в режиме совместимости. Любой другой процесс может открыть этот файл несколько раз. Операция вызывает ошибку, если файл уже открыт другим процессом в любом другом режиме кроме режима совместимости.

Если вы указали методу OpenURL строку URL со схемой доступа ftp:, gopher: или http:, то он устанавливает связь с соответствующим объектом, используя для этого функцию InternetOpenUrl из программного интерфейса WinInet.

Установив соединение с объектом URL, метод OpenURL создает новый объект класса CGopherFile, CInternetFile или CHttpFile, в зависимости от типа URL и возвращает указатель на этот объект:


Тип URL

Указатель на объект класса

file:

CStdioFile

http:

CHttpFile

gopher:

CGopherFile

ftp:

CInternetFile

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

CFile -> CStdioFile -> CInternetFile -> CHttpFile

                                     |

                                     -> CGopherFile

Если вы указали URL, не соответствующий схемам доступа ftp:, gopher:, http: и file:, тогда метод OpenURL вернет значение NULL. В режиме отладки также будет выдано сообщение "Error: Unidentified service type:".

Через параметр dwContext методу OpenURL можно передать идентификатор контекста. Этот идентификатор будет использован при вызове функции обратного вызова.

Параметр dwFlags может содержать набор констант (флагов), объединенных оператором ИЛИ. В следующей таблице мы привели краткие описания этих констант:

Константа

Описание

INTERNET_FLAG_RELOAD

Загрузить данные из сети, даже если они уже получены и записаны в кэше

INTERNET_FLAG_DONT_CACHE

Не выполнять кэширование принимаемых данных

INTERNET_FLAG_SECURE

Используется при передаче запросов HTTP с использованием Secure Sockets Layer или PCT

INTERNET_OPEN_FLAG_USE_EXISTING_CONNECT

Указывает использовать установленное соединение с сервером для новых запросов, передаваемых методом OpenUrl. В противном случае каждый последующий вызов OpenUrl будет создавать новвый сеанс связи

INTERNET_FLAG_PASSIVE

Используется при работе с серверами FTP. Выполняется пассивное соединение с сервером. Более подробное описание смотрите в документации к Microsoft Visual C++

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

По умолчанию дополнительный заголовок не указывается. Указатель на строку, содержащую дополнительный заголовок запроса HTTP, передается через параметр pstrHeaders.

Длина дополнительного заголовка передается методу OpenURL через параметр dwHeadersLength. Если вы передаете заголовок в строке, заканчивающейся двоичным нулем, то достаточно указать в качестве параметра dwHeadersLength значение -1L.

Метод OpenURL предназначен для загрузки объектов из сети. Он не выполняет операции, специфические для протоколов FTP, HTTP, Gopher. Для этих целей вы должны использовать другие классы (чтобы открыть соединение определенного вида).


Метод PutFile


По сравнению с методом GetFile, метод PutFile выполняет обратную задачу. Он позволяет записать файл с локального компьютера на сервер FTP. Также как и метод GetFile, этот метод не позволяет контролировать или вмешиваться в процесс загрузки.

Если вы желаете иметь более полный контроль за загрузкой файла на сервер, вы должны использовать вместо метода PutFile два метода - метод OpenFile класса CFtpConnection и метод Write класса CInternetFile.

Метод OpenFile

открывает и создает файл на сервере FTP, а метод Write позволяет записывать в него порции данных из буфера на локальном компьютере. Приведем прототип метода PutFile:

BOOL PutFile(

   LPCTSTR pstrLocalFile,

   LPCTSTR pstrRemoteFile,

   DWORD dwFlags = FTP_TRANSFER_TYPE_BINARY,

   DWORD dwContext = 1

);

Параметр pstrLocalFile указывает имя файла, записанного на локальном компьютере, который передается на сервер FTP и записывается в файл, определенный параметром pstrRemoteFile.

Параметр dwFlags определяет метод передачи файла, а параметр dwContext - идентификатор контекста. Назначение этих параметров полностью соответствует соответствующим параметрам метода GetFile.



Метод QueryInfo


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

В состав класса входят три варианта метода QueryInfo, отличающихся набором параметров. Вы можете использовать тот метод, который наиболее подходит для выполнения ваших задач:

BOOL QueryInfo(

   DWORD dwInfoLevel,

   LPVOID lpvBuffer,

   LPDWORD lpdwBufferLength,

   LPDWORD lpdwIndex = NULL

) const;

 

BOOL QueryInfo(

   DWORD dwInfoLevel,

   CString& str,

   LPDWORD dwIndex = NULL

) const;

 

BOOL QueryInfo(

   DWORD dwInfoLevel,

   SYSTEMTIME* pSysTime,

   LPDWORD dwIndex = NULL

) const;

Тип информации, запрашиваемой методом QueryInfo, определяется параметром dwInfoLevel, одинаковым у всех трех реализаций метода. В качестве этого параметра следует указать комбинацию атрибута и флага, определяющего тип получаемой информации:

Флаг

Назначение

HTTP_QUERY_CUSTOM

Определяет имя заголовка и записывает его в буфер lpvBuffer

HTTP_QUERY_FLAG_REQUEST_HEADERS

Данный флаг позволяет узнать заголовок запроса который был передан на сервер

HTTP_QUERY_FLAG_SYSTEMTIME

Некоторые заголовки содержат дату и время. Чтобы не утруждать себя разбором строки с датой и временем следует указать данный флаг и воспользоваться третьим вариантом метода QueryInfo, который сразу занесет эти данные в структуру типа SYSTEMTIME.

HTTP_QUERY_FLAG_NUMBER

Для тех заголовков, которые имеют числовые значения вы можете воспользоваться флагом HTTP_QUERY_FLAG_NUMBER. В этом случае запрашиваемые данные будут получены в виде 32-битового числа.

В следующей таблице перечислены возможные атрибуты, которые можно указывать в параметре dwInfoLevel:

Значения параметра lpdwIndex и dwIndex

HTTP_QUERY_MIME_VERSION

HTTP_QUERY_CONTENT_TYPE

HTTP_QUERY_CONTENT_TRANSFER_ENCODING

HTTP_QUERY_CONTENT_ID

HTTP_QUERY_CONTENT_DESCRIPTION

HTTP_QUERY_CONTENT_LENGTH

HTTP_QUERY_ALLOWED_METHODS

HTTP_QUERY_PUBLIC_METHODS

HTTP_QUERY_DATE

HTTP_QUERY_EXPIRES

HTTP_QUERY_LAST_MODIFIED

HTTP_QUERY_MESSAGE_ID

HTTP_QUERY_URI

HTTP_QUERY_DERIVED_FROM

HTTP_QUERY_LANGUAGE

HTTP_QUERY_COST

HTTP_QUERY_WWW_LINK

HTTP_QUERY_PRAGMA

HTTP_QUERY_VERSION

HTTP_QUERY_STATUS_CODE

HTTP_QUERY_STATUS_TEXT

HTTP_QUERY_RAW_HEADERS

HTTP_QUERY_RAW_HEADERS_CRLF

<
Данные, полученные методом QueryInfo, записываются либо в буфер lpvBuffer, либо в строку str, либо в структуру типа SYSTEMTIME, в зависимости от того, какой из прототипов метода используется.

Если вы используете первый прототип метода, то размер буфера вы должны записать в переменной типа DWORD и передать указатель на нее через параметр lpdwBufferLength. После вызова метода в эту переменную будет записана длина строки минус единица.

Чтобы перебрать множественные заголовки с одним и тем же именем вы должны использовать индекс заголовка, который передается через параметр lpdwIndex (или dwIndex) и начинается с нулевого значения. Перед вызовом метода запишите в этот параметр индекс заголовка, который надо получить. После того как метод завершит свою работу, в него будет записан индекс следующего заголовка или значение ERROR_HTTP_HEADER_NOT_FOUND.

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


Метод QueryInfoStatusCode


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

BOOL QueryInfoStatusCode( DWORD& dwStatusCode ) const;

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

Фактически метод QueryInfoStatusCode является частным случаем метода QueryInfo, который получает информацию об атрибуте HTTP_QUERY_STATUS_CODE.

Код завершения заносится методом QueryInfoStatusCode в переменную, ссылка на которую передается через параметр dwStatusCode. В следующей таблице представлены группы кодов завершения:

Коды завершения

Описание

200-299

Успешное завершение

300-399

Информация

400-499

Ошибка запроса

500-599

Ошибка сервера

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

Коды завершения

Описание

200

Объект с данным адресом URL обнаружен

400

Неправильный запрос

404

Объект с данным адресом URL не обнаружен

405

Сервер не поддерживает запрашиваемый метод

500

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

503

Сервер перегружен



Метод QueryOption


Метод QueryOption, позволяет определить различные характеристики сеанса связи. В состав класса CInternetSession входят два метода QueryOption, которые имеют различные параметры. Ниже мы привели прототипы этих методов:

BOOL

QueryOption(

   DWORD dwOption,

   LPVOID lpBuffer,

   LPDWORD lpdwBufLen

) const;

 

BOOL

QueryOption(

   DWORD dwOption,

   DWORD& dwValue

) const;

В обоих прототипах метода QueryOption первый параметр dwOption типа DWORD определяет информацию, которую надо получить. Через параметр lpBuffer вы должны передать методу QueryOption указатель на буфер, а через параметр lpdwBufLen - размер этого буфера. Метод QueryOption помещает запрашиваемую информацию в буфер lpBuffer, а в lpdwBufLen записывает ее длину.

Если вы используете второй прототип метода QueryOption, то вместо буфера lpBuffer указываться переменная типа DWORD, адрес которой передается через параметр dwValue.

Некоторые наиболее интересные константы, используемые в качестве параметра dwOption, описаны ниже.



Метод ReadString


Метод ReadString считывает строку данных с сервера, ограниченную символом перевода на новую строку. Определены два варианта метода ReadString. Они различаются только способом записи полученной строки.

Первый вариант метода ReadString наиболее универсален. Он записывает полученные с сервера данные в строку класса CString, ссылка на которую передается через параметр rString:

virtual BOOL ReadString( CString& rString );

throw ( CInternetException );

Когда все данные получены, данный вариант метода ReadString возвращает значение FALSE.

Второй вариант метода ReadString записывает полученные данные в буфер pstr:

virtual LPTSTR ReadString( LPTSTR pstr, UINT nMax );

throw ( CInternetException );

Максимальный размер буфера вы должны указать в параметре nMax. В конце полученных данных записывается нулевой символ.

Когда все данные получены, метод ReadString, прототип которого приведен выше, возвращает значение NULL.

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



Метод SendRequest


Метод SendRequest позволяет передать подготовленный запрос на сервер WWW.

Определены два варианта метода SendRequest, различающиеся типом и количеством параметров. Все параметры метода являются необязательными и их можно не указывать:

BOOL SendRequest(

   LPCTSTR pstrHeaders = NULL,

   DWORD dwHeadersLen = 0,

   LPVOID lpOptional = NULL,

   DWORD dwOptionalLen = 0

);

throw ( CInternetException )

 

BOOL SendRequest(

   CString& strHeaders,

   LPVOID lpOptional = NULL,

   DWORD dwOptionalLen = 0

);

throw ( CInternetException )

Необязательные параметры pstrHeaders и dwHeadersLen или strHeaders, для второго варианта метода, можно использовать для передачи дополнительных заголовков запроса.

Вы можете записать дополнительные заголовки в строку strHeaders или передать указатель на них через параметр pstrHeaders. Если вы не используете строки и работаете с указателями, то вы также должны задействовать параметр dwHeadersLen записав в него размер заголовка.

Если вам требуется передать сразу после заголовка запроса какие-либо данные, вы можете передать методу указатель на них через параметр lpOptional. В этом случае вы также должны записать в параметр dwOptionalLen длину этих данных.

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



Метод SetOption


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

BOOL

SetOption(

   DWORD dwOption,

   LPVOID lpBuffer,

   DWORD dwBufferLength

);

 

BOOL

SetOption(

   DWORD dwOption,

   DWORD dwValue

);



Метод WriteString


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

virtual void WriteString( LPCTSTR pstr );

throw CInternetException( );

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



Методы OnPaint и OnQueryDragIcon


Методы OnPaint и OnQueryDragIcon используются приложением FtpView в случае его минимизации чтобы отображать на экране пиктограмму. Эти методы мы оставляем без изменения и рассматривать их не будем. Вы можете узнать о методах OnPaint и OnQueryDragIcon более подробно из 24 тома серии “Библиотека системного программиста”.


Методы OnPaint и OnQueryDragIcon создаются во время создания проекта с помощью MFC AppWizard и используются для отображении пиктограммы приложения в случае его минимизации. Данные методы одинаковы для всех приложений, созданных MFC AppWizard и имеющих пользовательский интерфейс на основе диалоговой панели. Поэтому сейчас мы не станем рассматривать их более подробно. Вы можете найти описание этих методов в первой книге, посвященной Microsoft Visual C++ и библиотеке MFC из серии “Библиотека системного программиста” (том 24).



Методы SetWriteBufferSize и SetReadBufferSize


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

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

BOOL SetWriteBufferSize( UINT nWriteSize );

BOOL SetReadBufferSize( UINT nReadSize );

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

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



Наш сервер WWW


В начале 1997 года мы открыли в сети Internet собственный сервер WWW. На нем вы можете получить подробную информацию о наших изданных книгах, а также о книгах, которые мы планируем издать в ближайшее время.

На сервере вы можете бесплатно получить архивы исходных текстов программ из книг серии “Библиотека системного программиста” и прочитать статьи, опубликованные нами в некоторых периодических изданиях. В дальнейшем мы планируем разместить на сервере ответы на вопросы читателей, отзывы читателей и другую полезную информацию.

Адреса нашего WWW-сервера:

:         http://www.glasnet.ru/~frolov

http://www.dials.ccas.ru/frolov



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


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



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


У приложения FtpView, рассмотренном в предыдущем разделе, есть как минимум один большой недостаток. В то время когда идет загрузка файла с сервера FTP, ход этого процесса никак не отображается и приложение создает впечатление зависшей программы. Только те, кто использует внешний модем, могут догадаться о работе приложения по интенсивному или не очень интенсивному миганию индикаторов на его лицевой панели. Если компьютер оборудован внутренним модемом, вы в принципе также можете следить за его состоянием, если разместите в панели управления Windows 95 (или Windows NT версии 4.0) индикатор работы модема.

Этот недостаток нашего приложения обусловлен устройством метода GetFile класса CFtpConnection, так как он не предусматривает возможности извещения приложения о загрузке части файла. Поэтому вместо использования метода GetFile мы предлагаем воспользоваться методом OpenFile класса CFtpConnection и методом Read класса CInternetFile. Метод OpenFile открывает файл, расположенный на сервере, а метод Read позволяет считывать открытый файл по частям.

Кроме того, метод GetFile сразу записывает файл, полученный с сервера, в файл на локальном диске компьютера. Метод Read записывает полученный фрагмент файла с сервера во временный буфер. Это позволяет, например, обеспечить дополнительную обработку данных по мере их получения.

Доработаем приложение FtpView так, чтобы оно показывало ход загрузки файлов с сервера FTP. Для этого сначала загрузите в редактор ресурсов диалоговую панель IDD_FTPVIEW_DIALOG. Немного увеличьте вертикальный размер панели и добавьте внизу панели линейный индикатор progress bar, выбрав для него идентификатор IDC_PROGRESS, и текстовую строку Progress (рис. 2.13). Фрагмент файла FtpView.rc, содержащий шаблон диалоговой панели мы привели в листинге 4.4.

Листинг 2.13. Фрагмент файла FtpView.rc

//////////////////////////////////////////////////////////////

//

// Dialog

//

IDD_FTPVIEW_DIALOG DIALOGEX 0, 0, 352, 215

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |


                      WS_SYSMENU

EXSTYLE WS_EX_APPWINDOW

CAPTION "FtpView"

FONT 8, "MS Sans Serif"

BEGIN

  PUSHBUTTON      "Connect",IDC_CONNECT,196,5,45,15

  PUSHBUTTON      "On top",IDC_ON_TOP,253,5,40,15

  DEFPUSHBUTTON   "OK",IDOK,305,5,40,15

  CONTROL    "List1",IDC_FTP_LIST,"SysListView32",LVS_REPORT |

              LVS_SORTASCENDING | LVS_ALIGNLEFT | WS_BORDER |

              WS_TABSTOP,5,25,340,133

  EDITTEXT    IDC_FTP_ADDRESS,5,5,183,14,ES_AUTOHSCROLL

  LTEXT       "Directory:",IDC_STATIC,7,171,33,8

  EDITTEXT    IDC_STATUS,44,169,301,15,ES_AUTOHSCROLL |

              ES_READONLY

  CONTROL     "Progress1",IDC_PROGRESS, "msctls_progress32",

              WS_BORDER, 43,193,302,14

  LTEXT       "Progress:",IDC_STATIC,7,196,30,8

END

Запустите MFC ClassWizard, перейдите на страницу Member Variables, выберите класс CFtpViewDlg и добавьте к нему управляющий объект для линейного индикатора. Для этого выберите из списка Control IDs идентификатор IDC_PROGRESS и нажмите кнопку Add Variable. Вам будет предложено привязать к линейному индикатору объект класса CProgressCtrl. Введите имя этого объекта. В нашем случае мы использовали имя m_LoadProgress. Нажмите кнопку OK и закройте MFC ClassWizard.

В классе CFtpViewDlg появится новый элемент m_LoadProgress. Он будет добавлен в блоке AFX_DATA, которым управляет MFC ClassWizard:

class CFtpViewDlg : public CDialog

{

// Construction

public:

   ~CFtpViewDlg();

   CFtpViewDlg(CWnd* pParent = NULL);

// Dialog Data

   //{{AFX_DATA(CFtpViewDlg)

   enum { IDD = IDD_FTPVIEW_DIALOG };

   CProgressCtrl   m_LoadProgress;

   . . .

   //}}AFX_DATA

   . . .

}

Кроме того, вносятся изменения в метод DoDataExchange класса CFtpViewDlg. В блоке AFX_DATA_MAP, в котором MFC ClassWizard размещает методы для обмена данными, добавляется новая строка. Она устанавливает связь между линейным индикатором IDC_PROGRESS и объектом m_LoadProgress:



DDX_Control(pDX, IDC_PROGRESS, m_LoadProgress);

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

Загрузите в текстовый редактор Microsoft Visual C++ метод OnInitDialog класса CFtpViewDlg и добавьте к нему вызовы методов SetRange и SetStep, как это показано на листинге 2.14. Обратите внимание, что эти методы вызываются для объекта m_LoadProgress, представляющего линейный индикатор IDC_PROGRESS.

Листинг 2.14. Фрагмент файла FtpViewDlg.cpp, метод OnInitDialog класса CFtpViewDlg

BOOL CFtpViewDlg::OnInitDialog()

{

   // Вызываем метод OnInitDialog базового класса CDialog

   CDialog::OnInitDialog();

   // Устанавливаем пиктограммы, которые будут отображаться

   // в случае минимизации диалоговой панели

   SetIcon(m_hIcon,TRUE);  // Пиктограмма стандартного размера

   SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера

   //=========================================================

   // Устанавливаем границы для линейного индикатора

   m_LoadProgress.SetRange(0, 100);

   // Устанавливаем шаг приращения для линейного индикатора

   m_LoadProgress.SetStep(10);

   //=========================================================

   // Выполняем инициализацию списка IDC_FTP_LIST

   //=========================================================

   . . .

}

Наибольшие изменения надо внести в метод FtpFileDownload класса CFtpViewDlg. Мы должны изменить способ загрузки файла с сервера - отказаться от использования метода GetFile и принимать файл по частям, используя методы OpenFile и Read. Так как в этом случае принимаемые данные не записываются в файл, а помещаются во временный буфер, необходимо организовать запись данных из буфера в файл на локальном диске. Для этого мы будем использовать возможности класса CFile. И наконец, по мере загрузки файла с сервера мы будем изменять состояние линейного индикатора IDC_PROGRESS.



Модифицированный метод FtpFileDownload класса CFtpViewDlg, выполняющий все описанные выше действия представлен в листинге 2.15.

Листинг 2.15. Фрагмент файла FtpViewDlg.cpp, метод FtpFileDownload класса CFtpViewDlg

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )

{

   BOOL fResult;

   CString sLocalFileName;

   // Определяем объект класса CFileDialog, представляющий

   // стандартную диалоговую панель Save As, в которой

   // по умолчанию выбрано имя файла sFileName

   CFileDialog mFileOpen(FALSE, NULL, sFileName);

   // Отображаем диалоговую панель Open и позволяем

   // пользователю выбрать с помощью нее один файл

   int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open -

   // по нажатию кнопки OK или Cancel

   if(result == IDCANCEL)

   {

      // Если пользователь отказался от выбора файлов и

      // нажал кнопку Cancel отображаем соответствующее

      // сообщение и возвращаем значение FALSE

      AfxMessageBox("File not selected");

      return FALSE;

   }

   else if(result == IDOK)

   {

      // Если пользователь нажал кнопку OK, определяем

      // имя выбранного файла

      sLocalFileName = mFileOpen.GetPathName();

   }

   //=========================================================

   // Формируем полное имя файла для загрузки его с

   // сервера FTP

   sFileName = sCurentDirectory + "/" + sFileName;

   //=========================================================

   // Чтобы узнать размер файла, записанного на сервере FTP,

   // создаем объект класса CFtpFileFind

   CFtpFileFind m_FtpFileFind(m_FtpConnection);

   // Выполняем поиск выбранного нами файла

   if(fResult =

      m_FtpFileFind.FindFile(_T(sFileName)))

   {

      // Если поиск закончился успешно, получаем его

      // характеристики

      fResult = m_FtpFileFind.FindNextFile();

      // Временная переменная для хранения размера файла



      DWORD dwLength = 0;

      // Определяем длину файла

      dwLength = m_FtpFileFind.GetLength();

      // В соответствии с размером файла устанавливаем новые

      // границы для линейного индикатора

      m_LoadProgress.SetRange(0, (int)(dwLength/READ_BLOCK) );

      // Устанавливаем шаг приращения для линейного индикатора

      m_LoadProgress.SetStep(1);

   }

   // Так как мы искали только один файл, заканчиваем поиск

   m_FtpFileFind.Close();

   // Определяем указатель на файл сервера

   CInternetFile* iFile;

   // Открываем выбранный нами файл на сервере FTP

   iFile = m_FtpConnection -> OpenFile(

      sFileName.GetBuffer(MIN_LEN_BUF),

      GENERIC_READ,

      FTP_TRANSFER_TYPE_BINARY

   );

   //=========================================================

   // Создаем и открываем файл на локальном диске компьютера

   CFile fLocalFile(

            sLocalFileName,

            CFile::modeCreate | CFile::modeWrite

         );

   //=========================================================

   // Временная переменная. В нее заносится количество байт,

   // считанное с файла на сервере

   UINT nReadCount = 0;

  

   // Создаем буфер для чтения файла с сервера FTP

   void* ptrBuffer;

   ptrBuffer = malloc( READ_BLOCK );

   // Считываем файл с сервера, записываем его в локальный

   // файл и изменяем текущее положение линейного индикатора

   do

   {

      // Если во время операции чтения возникнет исключение

      // CInternetException, то оно будет обработано

      // соответствующим блоком catch

      try

      {

         // Читаем из файла на сервере READ_BLOCK байт

         nReadCount = iFile -> Read( ptrBuffer, READ_BLOCK );

      }

      catch (CInternetException* pEx)

      {

         // Обрабатываем исключение CInternetException

         TCHAR szErr[1024];  // Временный буфер для сообщения

         // Выводим сообщение об ошибке



         if (pEx->GetErrorMessage(szErr, 1024))

            AfxMessageBox(szErr);

         else

            AfxMessageBox("GetFtpConnection Error");

         // Так как возникла ошибка, значит данные не считаны

         nReadCount = 0;

         // Удаляем исключение

         pEx->Delete();

      }

      // Записываем информацию, считанную из файла на сервере,

      // которая находится в буфере в локальный файл

      fLocalFile.Write( ptrBuffer, nReadCount );

      // Увеличиваем значение на линейном индикаторе

      m_LoadProgress.StepIt();

   // Продолжаем чтение файла с сервера до тех пор, пока не

   // будет достигнут конец файла

   } while (nReadCount == READ_BLOCK);

   // После окончания загрузки файла сбрасываем

   // линейный индикатор

   m_LoadProgress.SetPos( 0 );

   // Закрываем файл на сервере

   iFile -> Close();

   // Удаляем объект iFile

   delete iFile;

   // Закрываем файл на локальном компьютере

   fLocalFile.Close();

   // Освобождаем буфер ptrBuffer

   free( ptrBuffer );

   return fResult;

}

Постройте проект и запустите полученное приложение. Введите адрес сервера FTP и нажмите кнопку Connect. Далее выберите из списка файлов на сервере, файл для загрузке и сделайте по нему двойной щелчок левой кнопкой мыши. Как и ранее, вам будет предложено указать имя файла на локальном компьютере, в который будет записан файл с сервера FTP. Затем начнется процесс загрузки файла, ход которого будет отображаться линейным индикатором в нижней части диалоговой панели приложения (рис. 2.16).



Рис. 2.16. Загрузка файла с сервера FTP

Как вы уже могли заметить, основные изменения в исходных текстах приложения FtpView коснулись только метода FtpFileDownload класса CFtpViewDlg, который собственно и осуществляет загрузку файла с сервера FTP. Рассмотрим подробнее как работает этот метод.


Обработка исключения CInternetException


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

try

{

   // Вызов метода, который может стать причиной исключения

   . . .

}

catch (CInternetException* pEx)

{

   // Обрабатываем исключение CInternetException

   TCHAR szErr[1024];

   if (pEx->GetErrorMessage(szErr, 1024))

      AfxMessageBox(szErr);

   else

      AfxMessageBox("GetFtpConnection Error");

   // Удаляем исключение

   pEx->Delete();

}

Во всех наших примерах обработчики исключения CInternetException выполняют примерно одни и те же действия. Сначала определяется причина, вызвавшая исключение. Для этой цели вызывается метод GetErrorMessage. Этот метод записывает текстовое сообщение об ошибке в буфер, указанный первым параметром. Размер буфера должен быть указан во втором параметре метода. Если текстовое описание ошибки недоступно, тогда метод GetErrorMessage возвращает нулевое значение и мы отображаем на экране свое собственное сообщение об ошибке.

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

¨     Обратите внимание, что надо использовать именно метод Delete, а не оператор delete, название которого начинается с маленькой буквы.



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


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



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


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



Оператор HINTERNET


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

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

operator HINTERNET() const;



Орган управления Microsoft Web Browser


После того, как вы вставите орган управления Microsoft Web Browser в проект, он станет доступен в панели инструментов разработки диалоговых панелей. Выберите в окне Project Workspace страницу ResourceView (рис. 4.6). Как видите, приложение имеет три типа ресурсов - диалоговую панель IDD_LOOK_DIALOG, пиктограмму IDR_MAINFRAME и ресурс с описанием версии приложения VS_VERSION_INFO. Это стандартный набор ресурсов приложения с диалоговым интерфейсом, созданного при помощи MFC AppWizard.

Рис. 4.6. Ресурсы приложения Look

Загрузите в редактор ресурсов диалоговую панель IDD_LOOK_DIALOG приложения Look. Если панель инструментов Controls не появилась на экране, откройте ее. Для этого выберите из меню View главного окна Microsoft Visual C++ строку Toolbars. На экране появится диалоговая панель Toolbars (рис. 4.7). Найдите в списке Toolbars переключатель Controls и включите его. Затем нажмите кнопку Close.

Если в списке Toolbars вы не смогли обнаружить переключатель Controls, проверьте, загружена ли диалоговая панель в окно редактирования. Панели управления Microsoft Visual C++ являются контекстно-зависимыми. При работе с различными объектами доступны разные панели управления.

Рис. 4.7. Диалоговая панель Toolbars

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

Рис. 4.8. Диалоговая панель Look

В верхней части диалоговой панели IDD_LOOK_DIALOG поместите поле редактирования IDC_TITLE_BAR. В нем будет отображаться заголовок просматриваемой страницы WWW. Слева от поля редактирования поместите текстовую строку Title.

Непосредственно под полем IDC_TITLE_BAR разместите список IDC_COMBO_ADDRESS для выбора адреса URL. В списке Address введите адреса URL серверов WWW, которые можно посетить нашим навигатором. Мы использовали следующие адреса серверов:

http://www.glasnet.ru/~frolov

http://www.dials.ccas.ru/frolov

http://www.microsoft.com/

http://www.dials.ccas.ru/


Первые два адреса из представленного списка, указывают на сервер WWW авторов этой книги. Третий адрес принадлежит компании Microsoft, а последний, четвертый адрес, - фирме АО “ДиалогНаука”.

Вы можете ввести в этом списке адреса других, известных вам серверов, которые вы желаете посетить с помощью навигатора Look. Конечно, жестко “зашивать” адреса серверов WWW в приложении плохо, но нашей задачей является не создание законченного приложения, а изучение органа управления Microsoft Web Browser.

В дальнейшем вы можете расширить приложение Look и хранить адреса серверов в базе данных, организовав к ней доступ через драйвер ODBC или интерфейс DAO. Пример работы с базами данных через драйвера ODBC с помощью классов MFC мы приводили в 28 томе серии “Библиотека системного программиста”.

Слева от списка IDC_COMBO_ADDRESS расположите текстовую строку Address. А справа разместите четыре кнопки Navigate, Refresh, Stop и Exit. Присвойте им соответственно идентификаторы IDC_NAVIGATE, IDC_BUTTON_REFRESH, IDC_BUTTON_STOP и IDOK.

Обратите внимание на органы управления, представленные в панели инструментов Controls. В ней появилась новая пиктограмма
 (рис. 4.9), которая и представляет Microsoft Web Browser Control. Если в диалоговой панели Toolbars установлен переключатель Show ToolTips, который включает режим кратких подсказок, и вы поместите на указатель мыши на пиктограмму
, то через некоторое время появится название этого органа управления - Microsoft Web Browser.



Рис. 4.9. Панель инструментов Controls

Таким образом, вы можете разместить окно Microsoft Web Browser Control в обычной диалоговой панели точно также, как вы размещаете на ней текстовую строку, кнопку или переключатель. Поместите орган управления Microsoft Web Browser Control в центре диалоговой панели IDD_LOOK_DIALOG и присвойте ему идентификатор IDC_EXPLORER.

Как вы увидите позже, когда навигатор “Microsoft Web Browser Control” не отображает никакой информации, он имеет серый цвет и полностью сливается с самой диалоговой панелью. Чтобы выделить окно навигатора, создайте вокруг него группу с текстом World Wide Web. Эта группа будет отображаться как тонкая рамка с текстом в левом верхнем углу.



Непосредственно под навигатором IDC_EXPLORER разместите поле редактирования IDC_STATUS_TEXT, дополнительно обозначив его текстовой строкой Status и линейный индикатор IDC_PROGRESS с текстовой строкой Progress.

Поля редактирования IDC_TITLE_BAR и IDC_STATUS_TEXT будут использоваться только для отображения текста, поэтому вы можете указать для них в панели свойств атрибут Read-Only.

Файл ресурсов приложения Look представлен в листинге 4.5. В нем вы найдете шаблон диалоговой панели IDD_LOOK_DIALOG, команды для включения в ресурсы пиктограмм, информационный ресурс VS_VERSION_INFO и другие вспомогательные команды.

Листинг 4.5. Файл Look.rc

//Microsoft Developer Studio generated resource script.

//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

//////////////////////////////////////////////////////////////

#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

// Русские ресурсы

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)

   #ifdef _WIN32

      LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

      #pragma code_page(1251)

   #endif //_WIN32

#ifdef APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

    "#define _AFX_NO_OLE_RESOURCES\r\n"

    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

    "\r\n"

    "#if !defined(AFX_RESOURCE_DLL) ||

        defined(AFX_TARG_ENU)\r\n"



    "#ifdef _WIN32\r\n"

    "LANGUAGE 9, 1\r\n"

    "#pragma code_page(1252)\r\n"

    "#endif\r\n"

    "#include ""res\\Look.rc2\r\n"

    "#include ""afxres.rc\r\n"

    "#endif\0"

END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Пиктограмма приложения

//

IDR_MAINFRAME         ICON    DISCARDABLE     "res\\Look.ico"

//////////////////////////////////////////////////////////////

//

// Диалоговая панель IDD_LOOK_DIALOG

//

IDD_LOOK_DIALOG DIALOGEX 0, 0, 444, 253

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION

EXSTYLE WS_EX_APPWINDOW

CAPTION "Look"

FONT 8, "MS Sans Serif"

BEGIN

    CONTROL         "",IDC_EXPLORER,

                    "{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}",

                    WS_TABSTOP,10,44,425,180

    COMBOBOX        IDC_COMBO_ADDRESS,

                    47,20,185,50,CBS_DROPDOWN | CBS_SORT |

                    WS_VSCROLL | WS_GROUP | WS_TABSTOP

    PUSHBUTTON      "Refresh",IDC_BUTTON_REFRESH,285,20,40,15

    PUSHBUTTON      "Stop",IDC_BUTTON_STOP,330,20,40,15

    GROUPBOX        "World Wide Web",IDC_STATIC,5,34,435,195

    LTEXT           "Address:",IDC_STATIC,8,22,28,8

    CONTROL         "Progress1",IDC_PROGRESS,

                    "msctls_progress32",WS_BORDER,

                    327,236,112,12

    EDITTEXT        IDC_STATUS_TEXT,47,236,224,12,

                    ES_AUTOHSCROLL | ES_READONLY

    EDITTEXT        IDC_TITLE_BAR,47,3,392,13,

                    ES_AUTOHSCROLL | ES_READONLY

    LTEXT           "Title:",IDC_STATIC,8,6,16,8

    LTEXT           "Status:",IDC_STATIC,5,238,23,8

    LTEXT           "Progress:",IDC_STATIC,293,238,30,8

    PUSHBUTTON      "Navigate",IDC_NAVIGATE,240,20,40,15



    PUSHBUTTON      "Exit",IDOK,393,21,44,14

END

#ifndef _MAC

//////////////////////////////////////////////////////////////

//

// Информация о приложении

//

VS_VERSION_INFO VERSIONINFO

 FILEVERSION 1,0,0,1

 PRODUCTVERSION 1,0,0,1

 FILEFLAGSMASK 0x3fL

#ifdef _DEBUG

 FILEFLAGS 0x1L

#else

 FILEFLAGS 0x0L

#endif

 FILEOS 0x4L

 FILETYPE 0x1L

 FILESUBTYPE 0x0L

BEGIN

    BLOCK "StringFileInfo"

    BEGIN

        BLOCK "040904B0"

        BEGIN

            VALUE "CompanyName", "\0"

            VALUE "FileDescription", "LOOK MFC Application\0"

            VALUE "FileVersion", "1, 0, 0, 1\0"

            VALUE "InternalName", "LOOK\0"

            VALUE "LegalCopyright", "Copyright © 1997\0"

            VALUE "LegalTrademarks", "\0"

            VALUE "OriginalFilename", "LOOK.EXE\0"

            VALUE "ProductName", "LOOK Application\0"

            VALUE "ProductVersion", "1, 0, 0, 1\0"

        END

    END

    BLOCK "VarFileInfo"

    BEGIN

        VALUE "Translation", 0x409, 1200

    END

END

#endif    // !_MAC

//////////////////////////////////////////////////////////////

//

// DESIGNINFO

//

#ifdef APSTUDIO_INVOKED

GUIDELINES DESIGNINFO DISCARDABLE

BEGIN

    IDD_LOOK_DIALOG, DIALOG

    BEGIN

        LEFTMARGIN, 7

        RIGHTMARGIN, 437

        TOPMARGIN, 7

        BOTTOMMARGIN, 246

    END

END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Данные для иинициализации диалоговой панели IDD_LOOK_DIALOG

//

IDD_LOOK_DIALOG DLGINIT

BEGIN

   IDC_EXPLORER, 0x376, 128, 0

       0x0000, 0x0000, 0x002c, 0x0000, 0x027e, 0x0000, 0x0125,

       0x0000, 0x0001, 0x0000, 0x8201, 0x0000, 0x0000, 0x0000,



       0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0x002c,

       0x0000, 0x0000, 0x0000, 0x004c, 0x0000, 0x1401, 0x0002,

       0x0000, 0x0000, 0x00c0, 0x0000, 0x0000, 0x4600, 0x0000,

       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,

       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,

       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,

       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6248,

       0x004b,

   IDC_COMBO_ADDRESS, 0x403, 30, 0

       0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x672e, 0x616c,

       0x6e73, 0x7465, 0x722e, 0x2f75, 0x667e, 0x6f72, 0x6f6c,

       0x0076,

   IDC_COMBO_ADDRESS, 0x403, 32, 0

       0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x642e, 0x6169,

       0x736c, 0x632e, 0x6163, 0x2e73, 0x7572, 0x662f, 0x6f72,

       0x6f6c, 0x0076,

   IDC_COMBO_ADDRESS, 0x403, 26, 0

       0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x642e, 0x6169,

       0x736c, 0x632e, 0x6163, 0x2e73, 0x7572, 0x002f,

   IDC_COMBO_ADDRESS, 0x403, 26, 0

       0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x6d2e, 0x6369,

       0x6f72, 0x6f73, 0x7466, 0x632e, 0x6d6f, 0x002f,

   IDC_COMBO_ADDRESS, 0x403, 20, 0

       0x7468, 0x7074, 0x2f3a, 0x772f, 0x7777, 0x6d2e, 0x7063,

       0x632e, 0x6d6f, 0x002f, 0

END

#endif   

//////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED

   ///////////////////////////////////////////////////////////

   //

   // Generated from the TEXTINCLUDE 3 resource.

   //

   #define _AFX_NO_SPLITTER_RESOURCES

   #define _AFX_NO_OLE_RESOURCES

   #define _AFX_NO_TRACKER_RESOURCES

   #define _AFX_NO_PROPERTY_RESOURCES

   #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

      #ifdef _WIN32

         LANGUAGE 9, 1

         #pragma code_page(1252)

      #endif

      #include "res\Look.rc2"

      #include "afxres.rc"

   #endif

   ///////////////////////////////////////////////////////////

#endif    // not APSTUDIO_INVOKED



Идентификаторы ресурсов приложения Look определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Мы привели исходный текст файла resource.h в листинге 4.6.

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

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by Look.rc

//

#define IDD_LOOK_DIALOG                 102

#define IDR_MAINFRAME                   128

#define IDC_EXPLORER                    1000

#define IDC_BUTTON_BACK                 1002

#define IDC_BUTTON_NEXT                 1003

#define IDC_PROGRESS                    1003

#define IDC_BUTTON_REFRESH              1004

#define IDC_BUTTON_STOP                 1005

#define IDC_STATUS_TEXT                 1006

#define IDC_TITLE_BAR                   1007

#define IDC_NAVIGATE                    1008

#define IDC_COMBO_ADDRESS               1012

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

   #ifndef APSTUDIO_READONLY_SYMBOLS

      #define _APS_NEXT_RESOURCE_VALUE        131

      #define _APS_NEXT_COMMAND_VALUE         32771

      #define _APS_NEXT_CONTROL_VALUE         1010

      #define _APS_NEXT_SYMED_VALUE           101

   #endif

#endif


Приложение ConsoleFtp


Создайте новый проект. Выберите из меню File строку New. На экране появится диалоговая панель New. Выберите из списка, расположенного в этой панели строку Project Workspace и нажмите кнопку OK. Откроется диалоговая панель New Project Workspace. В качестве типа создаваемого приложения укажите Console Application и введите в поле Name имя проекта - ConsoleFtp. Затем нажмите кнопку Create. Microsoft Visual C++ создаст новый проект.

Мы будем использовать для нашего приложения классы библиотеки MFC. Чтобы настроить проект соответствующим образом, вы должны выбрать из меню Build строку Settings. На экране откроется диалоговая панель Project Settings, содержащая различные настройки проекта. Откройте на этой диалоговой панели страницу General, выберите из списка Microsoft Foundations Classes строку Use MFC in a Shared DLL и нажмите кнопку OK.

По умолчанию, новый проект не содержит ни одного файла. Создайте новый текстовый файл. Для этого еще раз выберите из меню File строку New и в открывшейся панели New укажите тип текстового файла - Text File. Нажмите кнопку OK. Откроется окно текстового редактора. Наберите в нем текст приложения, представленный нами в листинге 2.1. Сохраните набранный текст в файле под именем ConsoleFtp.cpp, записав его в каталог проекта.

Затем выберите из меню Insert строку Files into Project. На экране появится диалоговая панель Insert Files into Project. Выберите из списка файлов в этой панели файл ConsoleFtp.cpp и нажмите кнопку Add. Файл ConsoleFtp.cpp будет добавлен к проекту.

Листинг 2.1. Файл ConsoleFtp.cpp

//============================================================

// Приложение ConsoleFtp. Выполняет соединение с заданным

// сервером FTP

//

// (C) Фролов Г.В., 1997

// E-mail: frolov@glas.apc.org

// WWW:    http://www.glasnet.ru/~frolov

//         или

//         http://www.dials.ccas.ru/frolov

//============================================================

// Включаемый файл для библиотеки MFC

#include <afx.h>

// Включаемый файл для классов WinInet


#include <afxinet.h>

// Включаемый файл для консольного ввода/вывода

#include <iostream.h>

// Включаемый файл для стандартных функций

#include <stdlib.h>

//============================================================

// Главная функция приложения

//============================================================

int main(int argc, char* argv[])

{

// Если приложение запущено без параметра, отображаем на

   // экране формат вызова приложения

   if (argc != 2)

   {

      cout << "Programm format: ConsoleFtp <URL>" << endl;

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

      return -1;

   }

   // Записываем параметр, указанный при вызове приложения в

   // текстовую строку sUrlAdress. Он должен содержать URL

   // адрес сервера FTP

   CString sUrlAdress;

   sUrlAdress = argv[1];

  

   // Отображаем адрес сервера FTP на экране

   cout << "URL address: " << sUrlAdress << endl << endl;

   // Определяем переменные, в которые будут записаны

   // отдельные компоненты адреса, указанного пользователем

   // Имя сервера

   CString sServer;   

   // Имя объекта на который указывает адрес URL

   CString sObject;

   // Номер порта

   INTERNET_PORT nPort;

   // Тип сервиса или тип протокола

   DWORD dwServiceType;

  

   // Разбираем адрес URL, записанный в строке sUrlAdress

   if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,

                    sObject, nPort))

   {

      // В случае ошибки выводим сообщение и завершаем

      // работу приложения

      cout << "AfxParseURL Error" << endl;

     

      return -1;

   }

   // Проверяем, соответствует ли указанный адрес URL

   // серверу FTP. Для этого тип сервиса должен быть ftp://

   if(dwServiceType != AFX_INET_SERVICE_FTP)

   {

      // Если адрес не соответствует серверу FTP выводим

      // соответствующее предупреждение и завершаем приложение



      cout << " URL Address not FTP server" << endl;

        

      return -1;

   }

   // Указатель на объект класса CInternetSession

   CInternetSession* m_InternetSession = NULL;

   // Инициализируем сеанс работы с WinInet - создаем объект

   // класса CInternetSession catch

   m_InternetSession = new CInternetSession("Connecter");  

     

   // Исключения, вызванные в этом блоке try обрабатываются

   // следующим блоком catch

   try

   {

      // Определяем указатель на объект класса CFtpConnection

      CFtpConnection*  m_FtpConnection = NULL;

        

      // Пытаемся соединииться с сервером sServer

      m_FtpConnection =

         m_InternetSession -> GetFtpConnection( sServer );

      // Выводим сообщение об успешном соединении

      cout << "Connect to FTP server established" << endl;

      // Закрываем соединение с сервером FTP

      m_FtpConnection -> Close();

      // Удаляем объект m_FtpConnection

      delete( m_FtpConnection );

   }

   // Так как исключение в блоке try может быть вызвано только

   // методом GetFtpConnection, достаточно определить

   // обработчик исключения класса CInternetException

   catch (CInternetException* pEx)

   {

      // Обрабатываем исключение CInternetException

      TCHAR szErr[1024];  // временный буфер для сообщения

      // Выводим сообщение об ошибке

      if (pEx->GetErrorMessage(szErr, 1024))

         cout << "Error: " << szErr << endl << endl;

      // Удаляем исключение

      pEx->Delete();

   }

   // Завершаем сеанс связи

   m_InternetSession -> Close();

   // Удаляем объект m_InternetSession

   delete(m_InternetSession);

   return 0;

}

Постройте проект. Вы получите исполнимый файл приложения ConsoleFtp.exe. Запустите его на выполнение из командной строки MS-DOS Prompt (закрывать Windows не надо). В качестве параметра укажите URL адрес сервера FTP с которым вы желаете соединиться. Чтобы приложение правильно восприняло этот адрес, на забудьте указать в нем тип сервиса ftp.



Например, в ответ на следующую команду, приложение ConsoleFtp будет пытаться выполнить соединение с сервером FTP корпорации Microsoft:

ConsoleFtp.exe ftp://ftp.microsoft.com

Если указанный сервер FTP доступен, приложение выведет на экран сообщение об успешном соединении и завершит свою работу. Ниже мы привели сообщение приложения ConsoleFtp о соединении с сервером ftp.microsoft.com:

URL address: ftp://ftp.microsoft.com

Connect to FTP server established

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

URL address: ftp://failer

Error: The server name or address could not be resolved

В данном случае мы попытались установить соединение с сервером FTP по адресу ftp://failer, но такой сервер не был обнаружен.

Если вы желаете запустить приложение ConsoleFtp непосредственно из среды Microsoft Visual C++ или под отладкой, вы должны указать параметры для него в диалоговой панели Project Settings. Чтобы открыть эту панель выберите из меню Build строку Settings. Выберите в панели Project Settings страницу Debug (рис. 2.1), а затем из списка Category строку General.



Рис. 2.1. Диалоговая панель Project Settings, страница Debug

Теперь вы можете ввести аргументы для приложения в строке Program arguments. Когда вы запустите приложение из среды Microsoft Visual C++, ему будут переданы именно эти параметры.


Приложение ConsoleHTTP


В разделе “Приложение Console FTP“ мы привели приложение ConsoleFTP, которое соединяется с заданным сервером FTP. Для этого в нем сначала создается объект класса CInternetSession, представляющий сеанс работы с WinInet, а затем с помощью метода GetFtpConnection предпринимается попытка соединиться с сервером по заданному адресу. Если установить соединение не удается, например, из - за того, что адрес с таким адресом не существует, то метод GetFtpConnection вызывает исключение CInternetException:

// Инициализируем сеанс работы с WinInet

CInternetSession* m_InternetSession = NULL;

m_InternetSession = new CInternetSession("Connecter");  

try

{

   // Пытаемся соединиться с FTP - сервером sServer

   CFtpConnection*  m_FtpConnection = NULL;

        

   m_FtpConnection =

         m_InternetSession -> GetFtpConnection( sServer );

   . . .

}

catch (CInternetException* pEx)

{

      // Обрабатываем исключение CInternetException

      . . .

}

Таким образом, о существовании FTP сервера sServer можно судить по тому, вызвал ли метод GetFtpConnection исключение CInternetException. Если исключение не появилось и метод GetFtpConnection вернул не нулевое значение, значит сервер FTP существует и доступен. А если исключение все же было вызвано, значит, скорее всего, сервера с заданным именем просто не существует.

Как вы знаете, класс CInternetSession содержит в себе три метода для соединения с серверами FTP, WWW и Gopher. Это методы GetFtpConnection, GetHttpConnection и GetGopherConnection. Казалось бы достаточно заменить в исходных текстах приложения FtpConnection вызов метода GetFtpConnection на GetHttpConnection и вы сможете использовать это же приложения для поиска серверов WWW.

Проведите небольшой эксперимент и внесите в исходные тексты проекта FtpConnection соответствующие изменения:

// Инициализируем сеанс работы с WinInet

CInternetSession* m_InternetSession = NULL;

m_InternetSession = new CInternetSession("Connecter");  


try

{

   // Пытаемся соединиться с WWW - сервером sServer

   CHttpConnection*  m_HttpConnection = NULL;

        

   m_HttpConnection =

         m_InternetSession -> GetHttpConnection( sServer );

   . . .

}

catch (CInternetException* pEx)

{

      // Обрабатываем исключение CInternetException

      . . .

}

Постройте проект и запустите полученное приложение. Попытайтесь соединиться с помощью приложения с различными серверами WWW. Очень скоро вы обнаружите что соединение выполняется “успешно” даже с несуществующими серверами.

Чтобы быть уверенным, что сервер WWW по данному адресу не существует или недоступен, вы можете попытаться связаться с ним при помощи любой программы навигатора, например с помощью Microsoft Internet Explorer или Netscape Navigator.

Так, если вы запустите навигатор Microsoft Internet Explorer и попробуете просмотреть с его помощью сервер FTP по адресу http://home.frolov.com/, то скорее всего, получите предупреждающее сообщение, показанное нами на рисунке 3.1. Увы, пока сервера FTP с таким именем не существует.



Рис. 3.1. Сервер http://home.frolov.com/ не найден

Изменим теперь приложение ConsoleFtp так, чтобы оно не только соединялось с сервером WWW, но и запрашивало с него информацию об определенном файле. Создайте новый проект ConsoleHttp таким же образом, как вы создавали проект ConsoleFtp. Укажите что приложение будет использовать библиотеку классов MFC.

Затем создайте новый текстовый файл ConsoleHttp.cpp, наберите в нем программный код, представленный в листинге 3.2 и включите его в проект. Чтобы ускорить набор файла ConsoleHttp.cpp за его основу вы можете взять файл ConsoleFtp.cpp из проекта ConsoleFtp.

Листинг 3.2. Файл ConsoleHttp.cpp

//============================================================

// Приложение ConsoleFtp. Выполняет соединение с заданным

// сервером WWW

//

// (C) Фролов Г.В., 1997

// E-mail: frolov@glas.apc.org

// WWW:    http://www.glasnet.ru/~frolov

//         или

//         http://www.dials.ccas.ru/frolov



//============================================================

// Включаемый файл для библиотеки MFC

#include <afx.h>

// Включаемый файл для классов WinInet

#include <afxinet.h>

// Включаемый файл для консольного ввода/вывода

#include <iostream.h>

// Включаемый файл для стандартных функций

#include <stdlib.h>

//============================================================

// Главная функция приложения

//============================================================

int main(int argc, char* argv[])

{

   // Если приложение запущено без параметра, отображаем на

   // экране формат вызова приложения

   if (argc != 2)

   {

      cout << "Programm format: ConsoleFtp <URL>" << endl;

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

      return -1;

   }

   // Записываем параметр, указанный при вызове приложения в

   // текстовую строку sUrlAdress. Он должен содержать URL

   // адрес сервера WWW

   CString sUrlAdress;

   sUrlAdress = argv[1];

  

   // Отображаем адрес URL на экране

   cout << "URL address: " << sUrlAdress << endl << endl;

   // Определяем переменные, в которые будут записаны

   // отдельные компоненты адреса, указанного пользователем.

   // Имя сервера

   CString sServer;   

   // Имя объекта на который указывает адрес URL

   CString sObject;

   // Номер порта

   INTERNET_PORT nPort;

   // Тип сервиса или тип протокола

   DWORD dwServiceType;

  

   // Разбираем адрес URL, записанный в строке sUrlAdress

   if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,

                    sObject, nPort))

   {

      // В случае ошибки выводим сообщение и завершаем

      // работу приложения

      cout << "AfxParseURL Error" << endl;

     

      return -1;

   }

   // Проверяем, соответствует ли указанный адрес URL

   // серверу WWW. Для этого тип сервиса должен быть http://



   if(dwServiceType != AFX_INET_SERVICE_HTTP)

   {

         cout << " URL Address not WWW server" << endl;

         return -2;

   }

   // Указатель на объект класса CInternetSession

   CInternetSession* m_InternetSession = NULL;

   // Инициализируем сеанс работы с WinInet - создаем объект

   // класса CInternetSession

   m_InternetSession = new CInternetSession("Connecter");  

   // Запрос для сервера WWW      

   CHttpFile* pFile=NULL;  

   // Определяем указатель на объект класса CFtpConnection

   CHttpConnection*  m_HttpConnection = NULL;

   // Исключения, вызванные в этом блоке try обрабатываются

   // следующим блоком catch

   try

   {

      // Пытаемся соединииться с сервером sServer

      m_HttpConnection =

         m_InternetSession -> GetHttpConnection( sServer );

      // Формируем запрос

      pFile =

         m_HttpConnection -> OpenRequest(

                              CHttpConnection::HTTP_VERB_HEAD,

                              sObject

                             );

      // Дополнительный заголовок запроса HTTP

      CString headerInfo(_T(

         "Accept: text/*\r\nUser-Agent: HTTP Example\r\n"));

  

      // Добавляем к запросу дополнительный заголовок

      if(pFile->AddRequestHeaders(headerInfo))

      {

         // Передаем запрос на сервер

         if(pFile->SendRequest())

         {

            DWORD dwReturnCode;

            // Определяем код завершения запроса

            if(pFile->QueryInfoStatusCode(dwReturnCode))

            {

               // Отображаем код завершения запроса на экране

               CString sMessage;

               sMessage.Format("%d",dwReturnCode);

               cout << "SendRequest: " << sMessage <<

                        endl << endl;

            }

            else

            {

               cout << "Request Error" << endl << endl;



            }

         }

      }

  

   }

   // Обрабатываем исключение CInternetException

   catch (CInternetException* pEx)

   {

      // Временный буфер для сообщения

      TCHAR szErr[1024]; 

      // Выводим сообщение об ошибке

      if (pEx->GetErrorMessage(szErr, 1024))

         cout << "Error: " << szErr <<endl <<endl;

      // Удаляем иссключение

      pEx->Delete();

   }

   // Закрываем запрос

   pFile -> Close();

   // Удаляем объект pFile из памяти

   if(pFile)

      delete pFile;

   // Закрываем соединение с сервером WWW

   m_HttpConnection -> Close();

   // Удаляем объект m_FtpConnection

   delete( m_HttpConnection );

   // Завершаем сеанс связи

   m_InternetSession -> Close();

   // Удаляем объект m_InternetSession

   delete(m_InternetSession);

   return 0;

}

Постройте только что созданный проект и запустите приложение, указав ему в качестве параметра адрес WWW страницы. Адрес должен быть полным - он должен определять метод доступа (в данном случае http://), имя сервера на котором страница расположена и полный путь к файлу страницы WWW.

Приложение соединится с сервером, на котором эта страница расположена и запросит о ней информацию. Результаты запроса будут выведены на экран. На рисунке 3.3 мы показали работу приложения ConsoleHttp, запросив с сервера http://dials.ccas.ru/ страницу с именем frolov.htm.



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


Приложение FtpView


Навигатор Microsoft Internet Explorer можно использовать для просмотра серверов FTP и загрузки с них файлов на локальный диск компьютера. Если вы запустите навигатор и введете для просмотра адрес какого-нибудь сервера FTP, например сервера Microsoft, то в окне навигатора будет показан список файлов и каталогов сервера (рис. 2.2).

Рис. 2.2. Microsoft Internet Explorer просматривает корневой каталог сервера FTP по адресу ftp.microsoft.com

В этом разделе мы создадим свое приложение, которое подобно навигатору Microsoft Internet Explorer может просматривать структуру каталогов серверов FTP и даже загружать с них файлы на диск локального компьютера.

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

Приложение, сформированное MFC AppWizard, будет иметь единственную диалоговую панель, которая отображается сразу после ее запуска. О том, как устроено приложение FtpView, вы можете прочитать в 24 томе серии “Библиотека системного программиста” в разделе “Приложение с главной диалоговой панелью”.



Приложение Look


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

Начнет работать MFC AppWizard. Вам будет предложено заполнить несколько диалоговых панелей MFC AppWizard, в которых определяются свойства создаваемого приложения. В первой такой панели, которая называется MFC AppWizard - Step 1, выберите тип пользовательского интерфейса для приложения. Переключатель What type of application would you like to create переведите в положение Dialog based. Нажмите кнопку Next. На экране появится следующая диалоговая панель MFC AppWizard - Step 2 of 4. В группе What OLE support would you like to include этой панели вы должны включить переключатель OLE controls.

¨ Если вы работаете в Microsoft Visual C++ версии 5.0, вы должны иметь в виду, что в диалоговой панели MFC AppWizard - Step 2 of 4 изменились названия некоторых полей и переключателей. Это связано с тем, что технология OLE стала именоваться как ActiveX. Так группа What OLE support would you like to include стала называться What ActiveX support would you like to include

 Все остальные параметры приложения можно оставить по умолчанию и нажать на кнопку Finish. На экране появится панель New Project Information, содержащая список свойств создаваемого приложения. Нажмите кнопку OK и MFC AppWizard создаст для вас проект Look.

Теперь надо добавить к проекту орган управления Microsoft Web Browser Control, с помощью которого приложение Look будет обмениваться данными с Internet.

Выберите из меню Insert строку Component. На экране появится диалоговая панель Component Gallery. Откройте страницу OLE Controls этой панели (рис. 4.2). На странице OLE Controls представлены органы управления OCX, которые вы можете использовать в ваших приложениях.




Рис. 4.2. Диалоговая панель Component Gallery

Среди органов управления, представленных на странице OLE Controls в диалоговой панели Component Gallery, выберите орган управления Microsoft Web Browser Control. Для большинства органов управления, представленных в панели Component Gallery, можно получить их описание. Для этого достаточно нажать кнопку ?. К сожалению, при выборе пиктограммы Microsoft Web Browser Control, кнопка ? остается заблокированной.

Когда вы устанавливаете Internet Explorer, орган управления ActiveX подключается к операционной системе. Microsoft Visual C++ обнаруживает установленные органы ActiveX и позволяет использовать их в своих приложениях. Фактически программный код органа управления ActiveX расположен в файле Shdocvw.dll, который вы можете обнаружить в системном каталоге Windows.

¨     Если вы используете Microsoft Visual C++ более ранней версии, чем 5.0, вы можете получить информацию об органе управления Microsoft Web Browser Control можно на WWW-сервере компании Microsoft, по адресу http://www.microsoft.com в документе Web Browsing Objects for C/C++ Developers. Начиная с версии 5.0 соответствующее описание включено в документацию Microsoft Visual C++.



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

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

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

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



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



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

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

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



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

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



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

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

Класс

Описание

CLookApp

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

CLookDlg

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

CWebBrowser

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


Приложение Parse


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

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

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

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

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

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

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


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

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

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

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

//////////////////////////////////////////////////////////////

//

// Dialog

//

IDD_PARSEURL_DIALOG DIALOGEX 0, 0, 241, 210

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |

                      WS_SYSMENU

EXSTYLE WS_EX_APPWINDOW

CAPTION "ParseURL"

FONT 8, "MS Sans Serif"

BEGIN

  DEFPUSHBUTTON   "OK",IDOK,184,183,50,14

  EDITTEXT  IDC_EDIT_URL_ADDRESS,7,27,227,15, ES_AUTOHSCROLL

  LTEXT     "URL Address",IDC_STATIC,7,15,43,8

  EDITTEXT  IDC_EDIT_SERVICE_TYPE,7,73,227,15,ES_AUTOHSCROLL |

                                              ES_READONLY

  LTEXT     "Service type",IDC_STATIC,7,61,40,8

  EDITTEXT  IDC_EDIT_SERVER_NAME,7,110,227,15,ES_AUTOHSCROLL |

                                              ES_READONLY



  LTEXT     "Server name",IDC_STATIC,7,97,41,8

  EDITTEXT  IDC_EDIT_OBJECT_NAME,7,147,227,15,ES_AUTOHSCROLL |

                                              ES_READONLY

  LTEXT     "Object name",IDC_STATIC,7,135,41,8

  EDITTEXT  IDC_EDIT_PORT_NUMBER,7,182,52,15, ES_AUTOHSCROLL |

                                              ES_READONLY

  LTEXT     "Port number",IDC_STATIC,7,170,39,8

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

END

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

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

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by ParseURL.rc

//

#define IDM_ABOUTBOX                    0x0010

#define IDD_ABOUTBOX                    100

#define IDS_ABOUTBOX                    101

#define IDD_PARSEURL_DIALOG             102

#define IDR_MAINFRAME                   128

#define IDC_EDIT_URL_ADDRESS            1000

#define IDC_EDIT_SERVICE_TYPE           1001

#define IDC_EDIT_SERVER_NAME            1002

#define IDC_EDIT_OBJECT_NAME            1003

#define IDC_EDIT_PORT_NUMBER            1004

#define IDC_BUTTON_CONVERT              1005

// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        129

#define _APS_NEXT_COMMAND_VALUE         32771

#define _APS_NEXT_CONTROL_VALUE         1006

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif

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

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



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

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

CParseURLDlg::CParseURLDlg(CWnd* pParent /*=NULL*/)

   : CDialog(CParseURLDlg::IDD, pParent)

{

   //{{AFX_DATA_INIT(CParseURLDlg)

   m_ObjectName = _T("");

   m_PortNumber = _T("");

   m_ServerName = _T("");

   m_ServiceType = _T("");

   m_UrlAddress = _T("");

   //}}AFX_DATA_INIT

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

}

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

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

void CParseURLDlg::DoDataExchange(CDataExchange* pDX)

{

   CDialog::DoDataExchange(pDX);

   //{{AFX_DATA_MAP(CParseURLDlg)

   DDX_Text(pDX, IDC_EDIT_OBJECT_NAME, m_ObjectName);

   DDX_Text(pDX, IDC_EDIT_PORT_NUMBER, m_PortNumber);

   DDX_Text(pDX, IDC_EDIT_SERVER_NAME, m_ServerName);

   DDX_Text(pDX, IDC_EDIT_SERVICE_TYPE, m_ServiceType);

   DDX_Text(pDX, IDC_EDIT_URL_ADDRESS, m_UrlAddress);

   //}}AFX_DATA_MAP

}

Мы разместили на диалоговой панели IDD_PARSEURL_DIALOG кнопку Convert. Еще раз воспользуйтесь MFC ClassWizard и добавьте к классу CParseURLDlg обработчик сообщений от этой кнопки. Он будет вызываться когда пользователь будет нажимать на кнопку. MFC ClassWizard предложит вам назвать соответствующий метод именем OnButtonConvert. Согласитесь с этим предложением и в таблице сообщений класса CParseURLDlg появится новая макрокоманда:



BEGIN_MESSAGE_MAP(CParseURLDlg, CDialog)

   //{{AFX_MSG_MAP(CParseURLDlg)

   . . .

   ON_BN_CLICKED(IDC_BUTTON_CONVERT, OnButtonConvert)

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

Кроме того, в определении класса CParseURLDlg будет добавлено описание метода OnButtonConvert. Мы привели определение класса CParseURLDlg в листинге 1.5.

Листинг 1.5. Фрагмент файла Parse.h, определение класса CParseURLDlg

class CParseURLDlg : public CDialog

{

// Construction

public:

   CParseURLDlg(CWnd* pParent = NULL);  

// Dialog Data

   //{{AFX_DATA(CParseURLDlg)

   enum { IDD = IDD_PARSEURL_DIALOG };

   CString   m_ObjectName;

   CString   m_PortNumber;

   CString   m_ServerName;

   CString   m_ServiceType;

   CString   m_UrlAddress;

   //}}AFX_DATA

   // ClassWizard generated virtual function overrides

   //{{AFX_VIRTUAL(CParseURLDlg)

   protected:

   virtual void DoDataExchange(CDataExchange* pDX);  

   //}}AFX_VIRTUAL

// Implementation

protected:

   HICON m_hIcon;

   // Generated message map functions

   //{{AFX_MSG(CParseURLDlg)

   virtual BOOL OnInitDialog();

   afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

   afx_msg void OnPaint();

   afx_msg HCURSOR OnQueryDragIcon();

   afx_msg void OnButtonConvert();

   //}}AFX_MSG

   DECLARE_MESSAGE_MAP()

};

Определение метода OnButtonConvert размещается в файле Parse.cpp вместе с остальными методами класса CParseURLDlg. MFC ClassWizard создает только шаблон метода, который не выполняет никаких действий. Вы должны самостоятельно доработать метод OnButtonConvert, загрузив его в текстовый редактор Microsoft Visual C++. В листинге 1.6 мы привели фрагменты файла Parse.cpp с определением метода OnButtonConvert класса CParseURLDlg.

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

void CParseURLDlg::OnButtonConvert()

{

   // Получаем адрес, введенный в диалоговой панели

   UpdateData(TRUE);

   CString sServer;     // Имя сервера



   CString sObject;     // Имя объекта (с каталогами)

   INTERNET_PORT nPort; // Номер порта TCP/IP

   DWORD dwServiceType;

// Тип сервиса или протокола

   // Разбираем адрес URL, записанный в строке m_UrlAddress

   if (!AfxParseURL(m_UrlAddress, dwServiceType, m_ServerName,

                    m_ObjectName, nPort))

   {

      AfxMessageBox("AfxParseURL Error");

      m_ObjectName = "";

      m_PortNumber = "";

      m_ServerName = "";

      m_ServiceType = "";

   }

   else

   {

      switch(dwServiceType)

      {

         case AFX_INET_SERVICE_FTP:

            m_ServiceType = "AFX_INET_SERVICE_FTP";

            break;

         case AFX_INET_SERVICE_HTTP:

            m_ServiceType = "AFX_INET_SERVICE_HTTP";

            break;

         case AFX_INET_SERVICE_GOPHER:

            m_ServiceType = "AFX_INET_SERVICE_GOPHER";

            break;

           

         case AFX_INET_SERVICE_FILE:

            m_ServiceType = "AFX_INET_SERVICE_FILE";

            break;        

         default:

            m_ServiceType.Format("%d", dwServiceType);

      }

        

      m_PortNumber.Format("%d", nPort);

   }

   // Выводим полученные значения в диалоговой панелли

   UpdateData(FALSE);

}

Для наглядности во время запуска приложения мы выполняем инициализацию поля ввода адреса URL, записывая в него строку http://host:80/bin/programm/main.htm. Этот адрес содержит все возможные элементы - тип протокола http, имя сервера host, номер порта 80, а также путь и имя объекта /bin/programm/main.htm, на который и указывает данный адрес URL.

Соответствующий программный код, инициализирующий строку адреса надо добавить к методу OnInitDialog класса CParseURLDlg. Мы привели исходный текст этого метода в листинге 1.7.

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

BOOL CParseURLDlg::OnInitDialog()



{

   CDialog::OnInitDialog();

   // . . .

   SetIcon(m_hIcon, TRUE);         // Set big icon

   SetIcon(m_hIcon, FALSE);      // Set small icon

  

   // Инициализируем поле адреса URL

   m_UrlAddress = "http://host:80/bin/programm/main.htm";

   // Отображаем строку m_UrlAddress в диалоговой панели

   UpdateData(FALSE);

  

   return TRUE;

}

Теперь в исходный текст приложения внесены все необходимые изменения. Вы можете построить проект и запустить приложение. На экране появится диалоговая панель ParseURL, внешний вид которой мы уже приводили ранее на рисунке 4.4.

Изначально в поле URL Address отображается адрес http://host:80/bin/programm/main.htm, который мы взяли в качестве примера. Вы можете изменить его по своему усмотрению - поменять тип протокола обмена, имя сервера, путь и имя объекта и номер порта. Вы даже можете попытаться ввести неправильный адрес, не соответствующий формату адресов URL.

Нажмите кнопку Convert. Приложение разберет введенный вами адрес URL на составные части и выведет их в других полях диалоговой панели. Так в поле Service type будет показан тип сервиса (протокола обмена). Это может быть либо текстовая строка, либо число. В поле Server name вы увидите имя сервера, а в поле Object name - имя объекта, включая путь. Если в адрес URL включен номер порта, то он будет показан в поле Port number.

Если вы введете в поле адреса URL неправильную строку, то на экране появится сообщение об ошибке, а поля Service type, Server name, Object name и Port number будут очищены.

Когда вы закончите экспериментировать с разбором различных адресов URL завершите приложение. Для этого достаточно нажать кнопку OK.


Приложение WebHelp


Создайте новый проект под названием WebHelp. В качестве типа приложения выберите из списка Type строку Application. Наберите в редакторе исходный текст приложения и сохраните его в файле WebHelp.cpp (листинг 5.1). Чтобы быстрее набрать текст приложения, вы можете получить его, изменив исходный текст приложения MFMenu, которое мы приводили в 24 томе.

Включите файл WebHelp.cpp в проект. Теперь надо перейти к созданию ресурсов, используемых в приложении.

Листинг 5.1. Файл WebHelp.cpp

//============================================================

// Приложение WebHelp: справочная система на основе WWW

//

// (C) Фролов Г.В., 1997

// E-mail: frolov@glas.apc.org

// WWW:    http://www.glasnet.ru/~frolov

//         или

//         http://www.dials.ccas.ru/frolov

//============================================================

// Включаемый файл для MFC

#include <afxwin.h>

#include "WebHelpRes.h"

//=====================================================

// Класс CWebHelpApp - главный класс приложения

//=====================================================

class CWebHelpApp : public CWinApp

{

public:

   // Мы будем переопределять метод InitInstance,

   // предназначенный для инициализации приложения

   virtual BOOL InitInstance();

};

 

// Создаем объект приложение класса CWebHelpApp

CWebHelpApp WebHelpApp;

 

//=====================================================

// Класс CWebHelpWindow - представляет главное окно

//=====================================================

class CWebHelpWindow : public CFrameWnd

{

public:

   // Объявляем конструктор класса CWebHelpWindow

   CWebHelpWindow();

   // Объявляем методы для обработки команд меню

   afx_msg void WebHelpCommand();

   afx_msg void AboutCommand();

   afx_msg void ExitAppCommand();

   // Макрокоманда необходима, так как класс

   // CWebHelpWindow обрабатывает сообщения

   DECLARE_MESSAGE_MAP()   

};

//=====================================================


// Метод MenuCommand

// Обрабатывает команду ID_TEST_BEEP

//=====================================================

void CWebHelpWindow::WebHelpCommand()

{

   // Запускаем навигатор для просмотра справочной

   // информации, записанной на сервере WWW

   ShellExecute(NULL, "open",

      "http://www.dials.ccas.ru/frolov/rwin/webhelp.htm",

      NULL, NULL, SW_SHOWNORMAL);

}

//=====================================================

// Метод MenuCommand

// Обрабатывает команду ID_TEST_BEEP

//=====================================================

void CWebHelpWindow::AboutCommand()

{

   MessageBox("World Wide Web Help, \n(c) Frolov Grigory");  

}

//=====================================================

// Метод ExitApp

//=====================================================

void CWebHelpWindow::ExitAppCommand()

{

   DestroyWindow();

}

//=====================================================

// Таблица сообщений класса CWebHelpWindow

//=====================================================

BEGIN_MESSAGE_MAP(CWebHelpWindow, CFrameWnd)

   ON_COMMAND(ID_HELP_WEBHELP, WebHelpCommand)

   ON_COMMAND(ID_HELP_ABOUT,   AboutCommand)

   ON_COMMAND(ID_FILE_EXIT,    ExitAppCommand)

END_MESSAGE_MAP()

//=====================================================

// Метод InitInstance класса CWebHelpApp

//=====================================================

BOOL CWebHelpApp::InitInstance()

{

   // Создаем объект класса CWebHelpWindow

   m_pMainWnd = new CWebHelpWindow();

   // Отображаем окно на экране

   m_pMainWnd -> ShowWindow(m_nCmdShow);

   // Обновляем содержимое окна

   m_pMainWnd -> UpdateWindow();

   return TRUE;

}

//=====================================================

// Конструктор класса CWebHelpWindow

//=====================================================

CWebHelpWindow::CWebHelpWindow()

{

   // Создаем окно приложения, соответствующее

   // данному объекту класса CWebHelpWindow

   Create(NULL, "World Wide Web Help", WS_OVERLAPPEDWINDOW,

         rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU));

}


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


Чтобы добавить к классу CFtpViewDlg метод FtpFileDownload, воспользуйтесь средствами MFC ClassWizard. В определении класса CFtpViewDlg появится объявление нового метода (листинг 2.10). Соответствующий фрагмент кода мы привели в листинге 3.3. Определение метода FtpFileDownload будет добавлено в файле FtpViewDlg.cpp. Исправьте этот метод так, как это показано в листинге 2.11.

Листинг 2.10. Файл FtpViewDlg.h, фрагмент определения класса CFtpViewDlg

//////////////////////////////////////////////////////////////

// Класс CFtpViewDlg

class CFtpViewDlg : public CDialog

{

. . .

   DECLARE_MESSAGE_MAP()

private:

   BOOL FtpFileDownload( CString sFileName );

};

Метод FtpFileDownload запрашивает у пользователя имя и расположение файла на локальном диске компьютера, в который будет загружен файл с сервера, выбранный из списка в главной диалоговой панели приложения (листинг 2.11). Когда имя файла определено, метод FtpFileDownload выполняет загрузку файла, считывая его непосредственно с сервера FTP.

Листинг 2.11. Файл FtpViewDlg.cpp, метод FtpFileDownload

//============================================================

// Метод FtpFileDownload класса CFtpViewDlg

// Загружает файл с сервера FTP

//============================================================

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )

{

   BOOL fResult;

   CString sLocalFileName;

   // Определяем объект класса CFileDialog, представляющий

   // стандартную диалоговую панель Save As, в которой

   // по умолчанию выбрано имя файла sFileName

   CFileDialog mFileOpen(FALSE, NULL, sFileName);

   // Отображаем диалоговую панель Open и позволяем

   // пользователю выбрать с помощью нее один файл

   int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open -

   // по нажатию кнопки OK или Cancel

   if(result == IDCANCEL)

   {

      // Если пользователь отказался от выбора файла и

      // нажал кнопку Cancel отображаем соответствующее


      // сообщение и возвращаем значение FALSE

      AfxMessageBox("File not selected");

      return FALSE;

   }

   else if(result == IDOK)

   {

      // Если пользователь нажал кнопку OK, определяем

      // имя выбранного файла

      sLocalFileName = mFileOpen.GetPathName();

   }

   sFileName = sCurentDirectory + "/" + sFileName;

   fResult =

     m_FtpConnection ->

        GetFile(sFileName.GetBuffer(MIN_LEN_BUF),

                sLocalFileName.GetBuffer(MIN_LEN_BUF),

                FALSE

        );

   return fResult;

}

Остается только исправить метод OnDblclkFtpList так, чтобы когда пользователь выберет файл из списка в главной диалоговой панели приложения, то выполнялся вызов метода FtpFileDownload класса CFtpViewDlg. На листинге 2.12 мы привели фрагмент файла FtpViewDlg.cpp, который содержит новый вариант метода OnDblclkFtpList.

Листинг 2.12. Файл FtpViewDlg.cpp, метод OnDblclkFtpList

//============================================================

// Метод OnDblclkFtpList класса CFtpViewDlg

// Переходит в выбранный каталог

//============================================================

void CFtpViewDlg::OnDblclkFtpList(NMHDR* pNMHDR,

   LRESULT* pResult)

{

   int       iTotalNumber; // Количество элементов списка

   CString   sSelItem;     // Название каталога

   CString   sLength_Dir;  // Длина файла или строка Dir

   // Блокируем список IDC_FTP_LIST

   m_FtpList.EnableWindow(FALSE);

   // Определяем количество элементов в списке IDC_FTP_LIST

   iTotalNumber = m_FtpList.GetItemCount();

   // Определяем, какой объект списка выбран

   for(int i = 0; i < iTotalNumber; i++)

   {

      // Атрибут LVIS_SELECTED установлен

      // у выбранного элемента списка

      if(LVIS_SELECTED == m_FtpList.GetItemState( i,

         LVIS_SELECTED ))

      {

         // Определяем название выбранного элемента списка

         // (имя файла или каталога)

         sSelItem = m_FtpList.GetItemText( i, 0 );



         // Считываем данные из колонки Length

         sLength_Dir = m_FtpList.GetItemText( i, 1 );

         if(sLength_Dir == "Dir") // Выбран каталог

         {

            // Переходим в выбранный каталог

            sCurentDirectory  =

               sCurentDirectory + "/" + sSelItem;

            // Меняем форму курсора

            CWaitCursor wait;  

            // Отображаем содержимое выбранного каталога

            DirectoryView();

            // Отображаем новый путь каталога

            m_Status.SetWindowText(sCurentDirectory);

         }

         else                     // Выбран файл

         {

            // Меняем форму курсора

            CWaitCursor wait;  

            FtpFileDownload(sSelItem);

         }

         break;

      }

   }

  

   // Снимаем блокировку списка IDC_FTP_LIST

   m_FtpList.EnableWindow(TRUE);

   *pResult = 0;

}

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

Однако теперь, когда вы делаете двойной щелчок левой клавишей мыши по имени файла, вместо предупреждающего сообщения на экране появляется стандартная диалоговая панель Save As с предложением сохранить выбранный файл на локальном диске компьютера.

На рисунке 2.14 мы показали предложение приложения FtpView сохранить файл с именем readme.txt, расположенный на сервере FTP корпорации Microsoft (адрес ftp.microsoft.com) в каталоге developer, в файле readme.txt на локальном диске компьютера в каталоге Library.



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

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



Рис. 2.15. Файл уже существует

Рассмотрим как работают методы OnDblclkFtpList и FtpFileDownload.


Работа приложения FtpView


Запустите приложение FtpView. На экране появится главная диалоговая панель приложения (рис. 2.11). В поле редактирования, расположенном в верхнем левом углу окна отображается адрес сервера FTP. В качестве примера мы привели адрес сервера АО “ДиалогНаука”. Вы можете изменить этот адрес по вашему усмотрению и ввести адрес известного вам сервера FTP.

Заметим, что вы должны указать только имя сервера без префикса ftp://, имен подкаталогов и имен файлов. Приложение FtpView не выполняет каких-либо проверок введенной строки и передает ее непосредственно методу FtpConnect класса CInternetSession.

Нажмите кнопку Connect. Приложение FtpView попытается установить связь с указанным сервером FTP. Если такая попытка закончится успешно, приложение выведет в диалоговой панели содержимое текущего каталога сервера FTP. Путь текущего каталога вы можете просмотреть в поле Directory. В зависимости от настройки сервера это может быть корневой каталог или один из его подкаталогов.

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

Если сервер FTP, с которым вы соединяетесь, не позволяет с ним работать незарегистрированным пользователям, то на экране появится соответствующее сообщение об ошибке. Вы можете доработать приложение FtpView так, чтобы оно позволяло задавать имя пользователя и пароль. Для этого достаточно передать их методу GetFtpConnection через параметры pstrUserName и pstrPassword.

Рис. 2.11. Приложение FtpView

Список содержимого текущего каталога сервера отображается в таблице, состоящей из четырех столбцов - Name, Length, Date и Time. В столбце Name отображаются имена каталогов и файлов, расположенных в данном каталоге. Остальные три столбца - Length, Date и Time включают информацию о размере, дате и времени создания или изменения соответствующего каталога или файла. Каталоги выделяются пиктограммой

, расположенной  перед его именем и строкой Dir в столбце Length, а файлы только пиктограммой
.

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

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

Рис. 2.12. Выбран файл README.DOC



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


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

Метод

Описание

Rename

Переименовывает файл, находящийся на сервере FTP

Remove

Удаляет файл с сервера FTP

PutFile

Записывает файл на сервер

GetFile

Позволяет загрузить определенный файл с сервера FTP

OpenFile

Открывает файл, расположенный на сервере FTP для последующего чтения или записи

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

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



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


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

Метод

Описание

SetCurrentDirectory

Позволяет перейти в заданный каталог на сервере FTP

GetCurrentDirectory

Определяет имя текущего каталога на сервере FTP. Смотри также метод GetCurrentDirectoryAsURL

GetCurrentDirectoryAsURL

Определяет адрес URL текущего каталога на сервере FTP

RemoveDirectory

Удаляет определенный каталог с сервера FTP

CreateDirectory

Создает на сервере FTP новый каталог

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



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


Приложение WebHelp должно содержать меню, из которого можно вызвать для просмотра в навигаторе WWW страницу со справочным текстом. Чтобы включить в проект меню, сначала создайте новый файл ресурсов. Выберите из меню File строку New, а затем из открывшейся диалоговой панели выберите строку Resource Script и нажмите кнопку OK.

Теперь выберите из меню Insert строку Resource. На экране появится диалоговая панель Insert Resource. Выберите из нее строку Menu и нажмите кнопку OK. Вы сможете разработать меню в диалоговом режиме. Чтобы быстрее перейти к редактированию меню, можно нажать кнопку New Menu (

) из панели управления Project.

Создайте меню Help, при выборе которой открывается меню, содержащее две строки - Web Help и About. В диалоговой панели свойств элементов меню (рис. 5.2) установите переключатель Help, тогда для этого меню будет установлено выравнивание по правому краю.

Затем в меню верхнего уровня добавьте строку File. В меню File поместите строку Exit. Строки Web Help и About из меню Help должны иметь идентификаторы ID_HELP_WEBHELP и ID_HELP_ABOUT, а строка Exit меню File - идентификатор ID_FILE_EXIT.

Рис. 5.2. Диалоговая панель Menu Item Properties

Сохраните файл ресурсов в файле с именем WebHelp.rc. Редактор ресурсов создает кроме самого файла ресурсов еще включаемый файл, в котором определяются константы, используемые в файле ресурсов. В нашем случае в нем определяются идентификаторы меню приложения. По умолчанию этот файл сохраняется под именем resource.h. Вы можете изменить его, выбрав из меню View строку Resource Includes. Для нашего приложения мы изменили имя включаемого файла для ресурсов на WebHelpRes.h. Содержимое этого файла представлено листингом 5.2.

Листинг 5.2. Файл WebHelpRes.h

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by WebHelp.rc

//

#define IDR_MENU                        101

#define ID_HELP_WEBHELP                 40003

#define ID_FILE_EXIT                    40005

#define ID_HELP_ABOUT                   40007


// Next default values for new objects

//

#ifdef APSTUDIO_INVOKED

   #ifndef APSTUDIO_READONLY_SYMBOLS

      #define _APS_NEXT_RESOURCE_VALUE        102

      #define _APS_NEXT_COMMAND_VALUE         40008

      #define _APS_NEXT_CONTROL_VALUE         1000

      #define _APS_NEXT_SYMED_VALUE           101

   #endif

#endif

В листинге 5.3 мы привели файл ресурсов WebHelpRes.rc приложения. Этот файл был подготовлен редактором ресурсов Visual C++. Одна из первых строк файла содержит директиву #include, которой подключается файл WebHelpRes.h, содержащий описание идентификаторов ресурсов (листинг 5.2).

Среди прочих служебных строк, необходимых редактору ресурсов и компилятору Visual C++, вы можете обнаружить описание меню приложения IDR_MENU.

Листинг 5.3. Файл WebHelpRes.rc

//Microsoft Developer Studio generated resource script.

//

#include "WebHelpRes.h"

#define APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

//////////////////////////////////////////////////////////////

#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

// Russian resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

//////////////////////////////////////////////////////////////

//

// Menu

//

IDR_MENU MENU DISCARDABLE

BEGIN

    POPUP "File"

    BEGIN

        MENUITEM "Exit",         ID_FILE_EXIT

    END

    POPUP "Help", HELP

    BEGIN

        MENUITEM "Web Help",     ID_HELP_WEBHELP

        MENUITEM SEPARATOR

        MENUITEM "About",        ID_HELP_ABOUT

    END

END

#ifdef APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//



// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "WebHelpRes.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "\r\n"

    "\0"

END

#endif    // APSTUDIO_INVOKED

#endif    // Russian resources

//////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 3 resource.

//

//////////////////////////////////////////////////////////////

#endif    // not APSTUDIO_INVOKED

Теперь проект готов. Вы можете построить его и запустить полученное приложение WebHelp. Внешний вид приложения представлен на рисунке 5.3. Окно приложения имеет меню File и Help. В меню File расположена только одна строка Exit, выбрав которую вы можете завершить приложение. Меню Help состоит из двух строк - Web Help и About.



Рис. 5.3. Приложение WebHelp

Если вы выберите строку About, то на экране появится временная диалоговая панель с информацией об приложении. Когда же вы выбираете из меню Help строку Web Help, будет запущена программа навигатор, которая используется вами по умолчанию и в нее будет загружена WWW страница, расположенная по адресу http://www.dials.ccas.ru/frolov/rwin/webhelp.htm. Мы специально подготовили эту страницу, расположив ее на нашем сервере и поместили на ней краткие инструкции по работе с программой.

Обратите внимание, что описанная нами программа работает вне зависимости от того, какой навигатор вы используете - Microsoft Internet Explorer, Netscape Navigator или какую-либо другую программу.


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


Загрузите шаблон диалоговой панели IDD_FTPVIEW_DIALOG приложения в редактор ресурсов (рис. 2.3).

В верхней левой части панели создайте поле редактирования для ввода адреса сервера FTP, с идентификатором IDC_FTP_ADDRESS. С правой стороны от этого поля расположите кнопку Connect с идентификатором IDC_CONNECT, кнопку On top с идентификатором IDC_ON_TOP и кнопку OK.

¨ По умолчанию MFC AppWizard размещает на диалоговой панели кнопки OK и Cancel. Кнопку OK вы можете использовать как есть, а кнопку Cancel мы предлагаем вам удалить, так как она будет нам не нужна.

В центре диалоговой панели разместите список (орган управления List Control) и присвойте ему идентификатор IDC_FTP_LIST. В этом списке будет отображаться содержимое каталогов сервера FTP.

Рис. 2.3. Редактирование диалоговой панели FtpView

Список List Control может отображать информацию в различных форматах. Это могут быть пиктограммы большого или маленького размера с подписями, отсортированные различным образом, или табличная информация разделенная на несколько колонок. Мы будем использовать последний из перечисленных форматов.

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

Рис. 2.4. Свойства списка IDC_FTP_LIST

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




Рис. 2.5. Свойства поля редактирования IDI_DIRECTORY

Кроме диалоговой панели IDD_FTPVIEW_DIALOG в ресурсы приложения FtpView входит пиктограмма IDR_MAINFRAME и ресурс, содержащий информацию о версии приложения VS_VERSION_INFO. Вы можете оставить ресурс VS_VERSION_INFO и пиктограмму IDR_MAINFRAME без изменения, но мы заменили стандартную пиктограмму приложений, созданных MFC AppWizard рисунком, который для нас нарисовал художник Алексей Абрамкин.

Вы должны добавить к ресурсам приложения еще две пиктограммы IDI_FILE и IDI_DIRECTORY, на которых изображены “лист бумаги” и “папка”. Эти пиктограммы будут использоваться для выделения файлов и каталогов в списке объектов каталога сервера FTP. Пиктограммы IDI_FILE и IDI_DIRECTORY обязательно должны иметь “маленькие” изображения размером 8 х 8 пикселов. Стандартные изображения 16 х 16 пикселов можно не рисовать - для нашего примера они не потребуются.

В следующей таблице мы привели изображения пиктограмм, которые используются в нашем приложении. В таблице все пиктограммы одинакового размера, но на самом деле пиктограммы IDI_FILE и IDI_DIRECTORY меньше чем пиктограмма IDR_MAINFRAME в два раза.

Пиктограмма

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

Имя файла



IDI_FILE

file.ico



IDI_DIRECTORY

director.ico



IDR_MAINFRAME

russian.ico

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

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



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

Файл ресурсов приложения FtpView представлен в листинге 2.2. В нем вы найдете шаблон диалоговой панели IDD_FTPVIEW_DIALOG, команды для включения пиктограмм, информационный ресурс VS_VERSION_INFO и другие вспомогательные команды.

Листинг 2.2. Файл FtpView.rc

//Microsoft Developer Studio generated resource script.



//

#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

//////////////////////////////////////////////////////////////

#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////

// Русские ресурсы

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)

#ifdef _WIN32

LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT

#pragma code_page(1251)

#endif //_WIN32

#ifdef APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// TEXTINCLUDE

//

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h\0"

END

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""\r\n"

    "\0"

END

3 TEXTINCLUDE DISCARDABLE

BEGIN

    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

    "#define _AFX_NO_OLE_RESOURCES\r\n"

    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

    "\r\n"

    "#if !defined(AFX_RESOURCE_DLL) ||

       defined(AFX_TARG_ENU)\r\n"

    "#ifdef _WIN32\r\n"

    "LANGUAGE 9, 1\r\n"

    "#pragma code_page(1252)\r\n"

    "#endif\r\n"

    "#include ""res\\FtpView.rc2"" \r\n"

    "#include ""afxres.rc"" \r\n"

    "#endif\0"

END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Диалоговая панель IDD_FTPVIEW_DIALOG

//

IDD_FTPVIEW_DIALOG DIALOGEX 0, 0, 352, 194

STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU

EXSTYLE WS_EX_APPWINDOW

CAPTION "FtpView"

FONT 8, "MS Sans Serif"

BEGIN

    DEFPUSHBUTTON   "OK",IDOK,305,5,40,15



    CONTROL         "List1",IDC_FTP_LIST,"SysListView32",

                    LVS_REPORT | LVS_SORTASCENDING |

                    LVS_ALIGNLEFT | WS_BORDER |

                    WS_TABSTOP,5,25,340,133

    EDITTEXT        IDC_FTP_ADDRESS,5,5,183,14,ES_AUTOHSCROLL

    PUSHBUTTON      "On top",IDC_ON_TOP,253,5,40,15

    PUSHBUTTON      "Connect",IDC_CONNECT,196,5,45,15

    LTEXT           "Directory:",IDC_STATIC,7,171,33,8

    EDITTEXT        IDC_STATUS,44,169,301,15,ES_AUTOHSCROLL | ES_READONLY

END

#ifndef _MAC

//////////////////////////////////////////////////////////////

//

// Информация о приложении

//

VS_VERSION_INFO VERSIONINFO

 FILEVERSION 1,0,0,1

 PRODUCTVERSION 1,0,0,1

 FILEFLAGSMASK 0x3fL

#ifdef _DEBUG

 FILEFLAGS 0x1L

#else

 FILEFLAGS 0x0L

#endif

 FILEOS 0x4L

 FILETYPE 0x1L

 FILESUBTYPE 0x0L

BEGIN

    BLOCK "StringFileInfo"

    BEGIN

        BLOCK "040904B0"

        BEGIN

          VALUE "CompanyName", "\0"

          VALUE "FileDescription", "FTPVIEW MFC Application\0"

          VALUE "FileVersion", "1, 0, 0, 1\0"

          VALUE "InternalName", "FTPVIEW\0"

          VALUE "LegalCopyright", "Copyright © 1997\0"

          VALUE "LegalTrademarks", "\0"

          VALUE "OriginalFilename", "FTPVIEW.EXE\0"

          VALUE "ProductName", "FTPVIEW Application\0"

          VALUE "ProductVersion", "1, 0, 0, 1\0"

        END

    END

    BLOCK "VarFileInfo"

    BEGIN

        VALUE "Translation", 0x409, 1200

    END

END

#endif    // !_MAC

//////////////////////////////////////////////////////////////

//

// DESIGNINFO

//

#ifdef APSTUDIO_INVOKED

GUIDELINES DESIGNINFO DISCARDABLE

BEGIN

    IDD_FTPVIEW_DIALOG, DIALOG



    BEGIN

        LEFTMARGIN, 2

        RIGHTMARGIN, 345

        TOPMARGIN, 4

        BOTTOMMARGIN, 187

    END

END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Пиктограммы приложения

//

IDR_MAINFRAME     ICON    DISCARDABLE     "res\\Russian.ico"

IDI_FILE          ICON    DISCARDABLE     "res\\File.ico"

IDI_DIRECTORY     ICON    DISCARDABLE     "res\\director.ico"

#endif   

//////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 3 resource.

//

#define _AFX_NO_SPLITTER_RESOURCES

#define _AFX_NO_OLE_RESOURCES

#define _AFX_NO_TRACKER_RESOURCES

#define _AFX_NO_PROPERTY_RESOURCES

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

#ifdef _WIN32

LANGUAGE 9, 1

#pragma code_page(1252)

#endif

#include "res\FtpView.rc2"

#include "afxres.rc" 

#endif

//////////////////////////////////////////////////////////////

#endif    // not APSTUDIO_INVOKED

Идентификаторы ресурсов приложения FtpView определены в файле resource.h. Этот файл создается автоматически редактором ресурсов Microsoft Visual C++. Исходный текст файла resource.h представлен в листинге 2.3.

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

//{{NO_DEPENDENCIES}}

// Файл создан Microsoft Developer Studio

// Используется в FtpView.rc

// Идентификаторы органов управления

#define IDD_FTPVIEW_DIALOG              102

#define IDR_MAINFRAME                   128

#define IDI_DIRECTORY                   130

#define IDI_FILE                        131

#define IDC_FTP_LIST                    1000

#define IDC_FTP_ADDRESS                 1001

#define IDC_VIEW                        1002

#define IDC_ON_TOP                      1003

#define IDC_CONNECT                     1004

#define IDC_STATUS                      1005

// Следующие значения идентификаторов используются по

// умолчанию для новых объектов

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        129

#define _APS_NEXT_COMMAND_VALUE         32771

#define _APS_NEXT_CONTROL_VALUE         1006

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif


Сеанс связи


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

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

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



Таблица сообщений класса CFtpViewDlg


Таблица сообщений класса CFtpViewDlg состоит из макрокоманд BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, определяющие сообщения обрабатываемые данным классом. Как видите, они расположены в блоке AFX_MSG_MAP, поэтому для управления ими используется ClassWizard.

Необработанные сообщения передаются базовому классу CDialog, так как он указан во втором параметре макрокоманды BEGIN_MESSAGE_MAP:

//============================================================

// Таблица сообщений класса CFtpViewDlg

//============================================================

BEGIN_MESSAGE_MAP(CFtpViewDlg, CDialog)

   //{{AFX_MSG_MAP(CFtpViewDlg)

   ON_WM_PAINT()

   ON_WM_QUERYDRAGICON()

   // Сообщение от кнопки Connect

   ON_BN_CLICKED(IDC_CONNECT, OnConnect)

   // Сообщение от кнопки On Top

   ON_BN_CLICKED(IDC_ON_TOP, OnOnTop)

   // Сообщение с кодом извещения NM_DBLCLK от списка

   ON_NOTIFY(NM_DBLCLK, IDC_FTP_LIST, OnDblclkFtpList)

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

Две первые макрокоманды, расположенные в данной таблице сообщений - ON_WM_PAINT и ON_WM_QUERYDRAGICON. При помощи ClassWizard вы можете обнаружить, что данные макрокоманды выполняют обработку сообщений WM_PAINT и WM_QUERYDRAGICON, вызывая для этого методы OnPaint и OnQueryDragIcon.

Для обработки сообщений от кнопок Connect  с идентификатором IDC_CONNECT и On Top с идентификатором IDC_ON_TOP макрокоманды ON_BN_CLICKED вызывают методы OnOnTop и OnConnect, определенные в классе CFtpViewDlg. Таблица сообщений класса CFtpViewDlg не содержит макрокоманд для обработки сообщений от кнопки OK, которая имеет идентификатор IDOK, и поэтому для нее вызывается метод OnOK базового класса CDialog.

Последняя макрокоманда таблицы сообщений - ON_NOTIFY. Она вызывает метод OnDblclkFtpList для обработки сообщений с кодом извещения NM_DBLCLK от списка IDC_FTP_LIST. Сообщение с таким кодом извещения вырабатывается списком, когда пользователь делает в нем двойной щелчок левой клавишей мыши.



Таблицы сообщений класса CLookDlg


Первые две макрокоманды, таблицы сообщений класса CLookDlg, присутствуют во всех приложениях, имеющих пользовательский интерфейс на основе диалоговой панели. Макрокоманды ON_WM_PAINT и ON_WM_QUERYDRAGICON выполняют обработку сообщений WM_PAINT и WM_QUERYDRAGICON, вызывая для этого методы OnSysCommand, OnPaint и OnQueryDragIcon.

Следующая макрокоманда ON_CBN_SELCHANGE обрабатывает сообщение от списка IDC_COMBO_ADDRESS, которое передается когда пользователь выбирает из него строку.

Последующие пять макрокоманд ON_BN_CLICKED вызывают обработчики сообщений от кнопок IDC_BUTTON_REFRESH, IDC_BUTTON_STOP, IDC_BUTTON_BACK, IDC_BUTTON_NEXT и IDC_NAVIGATE, когда пользователь нажимает на них:

BEGIN_MESSAGE_MAP(CLookDlg, CDialog)

   //{{AFX_MSG_MAP(CLookDlg)

   ON_WM_PAINT()

   ON_WM_QUERYDRAGICON()

   ON_CBN_SELCHANGE(IDC_COMBO_ADDRESS,

      OnSelchangeComboAddress)

   ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh)

   ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)

   ON_BN_CLICKED(IDC_BUTTON_BACK, OnButtonBack)

   ON_BN_CLICKED(IDC_BUTTON_NEXT, OnButtonNext)

   ON_BN_CLICKED(IDC_NAVIGATE, OnNavigate)

   //}}AFX_MSG_MAP

END_MESSAGE_MAP()

Таблица сообщений класса CLookDlg, выделенная в блоке BEGIN_MESSAGE_MAP, не содержит в себе ничего необычного, за исключением того, что в ней отсутствуют обработчики сообщений навигатора. Несмотря на то что мы использовали MFC ClassWizard для переопределения ряда методов навигатора, это не нашло никакого отражения в таблице сообщений.

Однако не спешите думать, что в наше приложение закрались какая-то ошибка. Просмотрите файл LookDlg.cpp до конца. Вы обнаружите еще одну таблицу сообщений, обозначенную макрокомандами BEGIN_EVENTSINK_MAP. Эта таблица также относится к классу CLookDlg, наследованному от базового класса CDialog, как это указано в заголовке таблицы:

BEGIN_EVENTSINK_MAP(CLookDlg, CDialog)

    //{{AFX_EVENTSINK_MAP(CLookDlg)

   ON_EVENT(CLookDlg, IDC_EXPLORER, 106 /* DownloadBegin */,

      OnDownloadBeginExplorer, VTS_NONE)


   ON_EVENT(CLookDlg,IDC_EXPLORER, 104 /* DownloadComplete */,
      OnDownloadCompleteExplorer, VTS_NONE)
   ON_EVENT(CLookDlg, IDC_EXPLORER, 108 /* ProgressChange */,
      OnProgressChangeExplorer, VTS_I4 VTS_I4)
   ON_EVENT(CLookDlg, IDC_EXPLORER, 100 /* BeforeNavigate */,
      OnBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
      VTS_PVARIANT VTS_BSTR VTS_PBOOL)
   ON_EVENT(CLookDlg,IDC_EXPLORER,200 /*FrameBeforeNavigate*/,
      OnFrameBeforeNavigateExplorer, VTS_BSTR VTS_I4 VTS_BSTR
      VTS_PVARIANT VTS_BSTR VTS_PBOOL)
   ON_EVENT(CLookDlg, IDC_EXPLORER, 113 /* TitleChange */,
      OnTitleChangeExplorer, VTS_BSTR)
   ON_EVENT(CLookDlg, IDC_EXPLORER, 103 /* Quit */,
      OnQuitExplorer, VTS_PBOOL)
   ON_EVENT(CLookDlg,IDC_EXPLORER, 102 /* StatusTextChange */,
      OnStatusTextChangeExplorer, VTS_BSTR)
   ON_EVENT(CLookDlg, IDC_EXPLORER, 101 /*NavigateComplete */,
      OnNavigateCompleteExplorer, VTS_BSTR)
   //}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
Все макрокоманды ON_EVENT  в этой таблице сообщений обрабатывают сообщения от навигатора IDC_EXPLORER. Макрокоманда ON_EVENT имеет пять параметров.
Первый параметр определяет имя класса которому принадлежит таблица сообщений. В данном случае все сообщения органа управления обрабатываются классом CLookDlg, которому и принадлежит таблица сообщений AFX_EVENTSINK_MAP.
Второй параметр определяет идентификатор органа управления сообщения от которого обрабатываются. В главной диалоговой панели нашего приложения есть только один орган управления ActiveX, сообщения которого обрабатываются - это навигатор IDC_EXPLORER.
Третий параметр содержит идентификатор метода, который перехватывается данной макрокомандой. Данный идентификатор представляет собой целое число, однозначно определяющее сообщение органа управления. MFC ClassWizard вставляет после этого идентификатора комментарий с именем соответствующего метода.
Имя метода, который определен в нашем приложении и вызывается для обработки данного сообщения, указывается в четвертом параметре макрокоманды. По умолчанию эти имена присваиваются MFC ClassWizard на основе имени сообщения.


И, наконец, последний пятый параметр макрокоманды определяет типы параметров метода обработчика. В качестве этого параметра может фигурировать один или несколько идентификаторов VTS_, разделенных символами пробела.
Описание идентификаторов VTS_ вы можете найти в документации Microsoft Visual C++. Сейчас мы опишем только те идентификаторы, которые фигурируют в таблице сообщений приложения:

Идентификатор
Тип параметра
VTS_I4
long
VTS_BSTR
const char*
VTS_PVARIANT
VARIANT*
VTS_PBOOL
BOOL FAR*
VTS_NONE
Параметры отсутствуют

Устройство приложения ConsoleFtp


Для приложения ConsoleFtp мы использовали операторы потокового ввода/вывода << и >>, а также функции new и free. Поэтому нам понадобилось включить файлы iostream.h и stdlib.h:

#include <iostream.h>

#include <stdlib.h>

Наряду со стандартными функциями библиотеки языка Си, наше приложение будет пользоваться классами WinInet, входящими в библиотеку MFC. Поэтому мы включили в исходный текст приложения файлы afx.h и afxinet.h:

#include <afx.h>

#include <afxinet.h>

Определяя главную функцию приложения, мы указали для нее параметры argc и argv. Они позволяют получить параметры, указанные при запуске приложения в командной строке:

int main(int argc, char* argv[])

{

}

¨     Напомним, что через argc передается количество параметров указанных приложению, а через argv - список указателей на эти параметры

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

if (argc != 2)

{

   cout << "Programm format: ConsoleFtp <URL>" << endl;

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

   return -1;

}

Параметр, указанный при вызове приложения, записываем в текстовую строку sUrlAdress. Если параметр задан правильно он должен содержать URL адрес сервера FTP:

CString sUrlAdress;

sUrlAdress = argv[1];

Отображаем адрес сервера FTP на экране, чтобы быть уверенным в том что он задан правильно:

cout << "URL address: " << sUrlAdress << endl << endl;

Далее мы объявляем временные переменные, которые будут использоваться при разборе адреса URL. Целью такого разбора будет выделение из адреса URL имени сервера FTP:

// Имя сервера

CString sServer;   

// Имя объекта на который указывает адрес URL

CString sObject;

// Номер порта


INTERNET_PORT nPort;

// Тип сервиса или тип протокола

DWORD dwServiceType;

Сам разбор адреса URL, записанного в строке sUrlAdress, выполняется с помощью функции AfxParseURL. Заметим, что это пожалуй единственная функция библиотеки MFC, относящаяся к WinInet, которая не требует предварительно создать сеанс связи - объект класса CInternetSession.

Исходный адрес URL передается функции AfxParseURL через параметр sUrlAdress, а результат записывается в переменные dwServiceType, sServer, sObject, nPort. Нас будут интересовать только две переменные dwServiceType и sServer. В переменную dwServiceType функция AfxParseURL записывает тип сервиса, указанный в адресе sUrlAdress, а в строку sServer - имя сервера:

if (!AfxParseURL(sUrlAdress, dwServiceType, sServer,

                 sObject, nPort))

{

   cout << "AfxParseURL Error" << endl;

   return -1;

}

if(dwServiceType != AFX_INET_SERVICE_FTP)

{

   cout << "URL Address not FTP server" << endl;

   return -1;

}

Если вы указали неправильный адрес URL или тип сервиса, не соответствующий серверу FTP, то на экран выводится предупреждающее сообщение и работа приложения завершается. Если адрес сервера указан верно, приложение создает сеанс связи с Internet. Для этого мы объявляем указатель на объект класса CInternetSession, а затем создаем сам объект, используя функцию new:

// Указатель на объект класса CInternetSession

CInternetSession* m_InternetSession = NULL;

// Инициализируем сеанс работы с WinInet - создаем объект

// класса CInternetSession catch

m_InternetSession = new CInternetSession("Connecter");  

Конструктор класса CInternetSession имеет много параметров, но мы все их используем по умолчанию, кроме первого параметра, через который мы задаем имя сеанса - Connecter. Если вы опустите и этот параметр конструктора, то в качестве имени сеанса будет взято имя приложения.

¨     В случае консольного приложения попытка определить его имя по умолчанию приводит к ошибке, которую вы сможете заметить, если запустите приложение в режиме отладки



Теперь можно попытаться соединиться с сервером FTP. Для этого следует вызвать метод GetFtpConnection класса CInternetSession только что созданного объекта m_InternetSession. В случае ошибки этот метод может вызвать исключение CInternetException. Поэтому вызов метода GetFtpConnection мы помещаем в блок try и определяем для него соответствующий блок catch, обрабатывающий исключение CInternetException:

// Исключения, вызванные в этом блоке try обрабатываются

// следующим блоком catch

try

{

   // . . .

}

catch (CInternetException* pEx)

{

   // . . .

}

Обработчик исключения CInternetException вызывает метод GetErrorMessage для полученного исключения. Он записывает во временный буфер szErr причину вызова исключения. Если текстовое описание исключения доступно, метод GetErrorMessage возвращает ненулевое значение. В этом случае мы отображаем полученный текст на экране:

// Обрабатываем исключение CInternetException

TCHAR szErr[1024];  // временный буфер для сообщения

// Выводим сообщение об ошибке

if (pEx->GetErrorMessage(szErr, 1024))

   cout << "Error: " << szErr << endl << endl;

Затем завершаем обработку исключения, вызывая для него метод Delete. Он удаляет объект, представляющий исключение, из оперативной памяти:

// Удаляем исключение

pEx->Delete();

Вернемся к блоку try. В нем приложение исполняет свою главную задачу - пытается установить соединение с сервером FTP. Адрес сервера FTP передается методу GetFtpConnection класса CInternetSession. Если соединение с сервером будет установлено, то этот метод вернет указатель на объект класса CFtpConnection и мы запишем его во временную переменную m_FtpConnection:

// Определяем указатель на объект класса CFtpConnection

CFtpConnection*  m_FtpConnection = NULL;

// Пытаемся соединиться с сервером sServer

m_FtpConnection =

   m_InternetSession -> GetFtpConnection( sServer );

Если попытка соединиться с сервером окажется неудачной, то метод GetFtpConnection вызовет исключение CInternetException и оно будет обработано соответствующим блоком catch.



В случае успешного соединения с сервером FTP, приложение отображает на экране соответствующее сообщение:

// Выводим сообщение об успешном соединении

cout << "Connect to FTP server established" << endl;

Больше приложение ConsoleFtp ничего не делает с сервером, поэтому закрываем соединение с сервером и удаляем соответствующий объект m_FtpConnection из памяти (этот объект создается методом GetFtpConnection):

// Закрываем соединение с сервером FTP

m_FtpConnection -> Close();

// Удаляем объект m_FtpConnection

delete( m_FtpConnection );

Перед окончанием работы приложения, завершаем сеанс связи, вызывая метод Close класса CInternetSession, а затем удаляем соответствующий объект из памяти, вызывая оператор delete:

// Завершаем сеанс связи

m_InternetSession -> Close();

// Удаляем объект m_InternetSession

delete(m_InternetSession);

Конечно, приложение ConsoleFtp демонстрирует только самые основные принципы использования классов WinInet. В следующем разделе мы приведем еще одно приложение для работы с серверами FTP. Приложение FtpView значительно сложнее чем ConsoleFtp. Оно позволяет пользователю просматривать структуру каталогов сервера FTP и названия записанных в них файлов. Несколько позже мы модифицируем приложение FtpView так, чтобы его можно было использовать для загрузки файлов и будем вносить в него небольшие изменения для демонстрации различных приемов работы с серверами FTP.


Устройство приложения ConsoleHttp


Рассмотрим теперь устройство приложения ConsoleHttp. Первая часть приложения ConsoleHttp практически совпадает с приложением ConsoleFtp, работа которого была рассмотрена нами выше в разделе “Устройство приложения ConsoleFtp”. Поэтому мы не будем на нем останавливаться. Скажем только что функция main сначала проверяет командную строку приложения. Если приложение было вызвано без параметров, или их больше чем один, то на экран выводится информация о формате вызова приложения.

Если пользователь правильно указал в командной строке ConsoleHttp только один параметр, он записывается в строку sUrlAdress и отображается на экране:

CString sUrlAdress;

sUrlAdress = argv[1];

Далее строка sUrlAdress передается для обработки функции AfxParseURL, которая разбирает адрес, записанный в ней на составные части и помещает их в переменные dwServiceType, sServer, sObject и nPort:

DWORD dwServiceType; // Тип сервиса

CString sServer;     // Имя сервера

CString sObject;     // Имя объекта на который указывает адрес

INTERNET_PORT nPort; // Номер порта

if(!AfxParseURL( sUrlAdress, dwServiceType,

                 sServer, sObject, nPort))

{

   cout << "AfxParseURL Error" << endl;

   return -1;

}

В том случае если пользователь указал в командной строке приложения неправильный адрес, не соответствующий формату URL, то функция AfxParseURL возвращает нулевое значение. Наше приложение отображает сообщение об ошибке и завершается с кодом -1.

Далее приложение проверяет, что указанный адрес соответствует адресу сервера WWW. В этом случае тип сервиса dwServiceType должен быть равен значению AFX_INET_SERVICE_HTTP. Если это не так, например, вместо адреса сервера WWW вы указали адрес сервера FTP, который начинается с ftp://, то на экран будет выведено сообщение об ошибке и приложение завершится с кодом возврата -2:

if(dwServiceType != AFX_INET_SERVICE_HTTP)

{

   cout << "URL Address not WWW server" << endl;

   return -2;

}

Если адрес сервера указан верно, приложение создает сеанс связи с Internet. Для этого мы объявляем указатель на объект класса CInternetSession а затем создаем сам объект, используя функцию new:


// Указатель на объект класса CInternetSession

CInternetSession* m_InternetSession = NULL;

// Инициализируем сеанс работы с WinInet - создаем объект

// класса CInternetSession catch

m_InternetSession = new CInternetSession("WWW Connecter");  

Конструктор класса CInternetSession имеет много параметров, но мы все их используем по умолчанию, кроме первого параметра, через который мы задаем имя сеанса - WWW Connecter.

Теперь можно попытаться соединиться с сервером и запросить с него файл. Для этого следует вызвать метод GetHttpConnection класса CInternetSession только что созданного объекта m_InternetSession. В случае ошибки этот метод может вызвать исключение CInternetException. Поэтому вызов метода GetHttpConnection мы помещаем в блок try и определяем для него соответствующий блок catch, обрабатывающий исключение CInternetException:

// Исключения, вызванные в этом блоке try обрабатываются

// следующим блоком catch

try

{

   // . . .

}

catch (CInternetException* pEx)

{

   // . . .

}

Обработчик исключения CInternetException вызывает метод GetErrorMessage для полученного исключения, который записывает во временный буфер szErr причину вызова исключения. Если текстовое описание исключения доступно, метод GetErrorMessage возвращает ненулевое значение. В этом случае мы отображаем полученный текст на экране.

Затем мы завершаем обработку исключения, вызывая для него метод Delete. Он удаляет объект, представляющий исключение из оперативной памяти:

// Удаляем исключение

pEx->Delete();

Рассмотрим блоку try более подробно. В нем приложение исполняет свою главную задачу - пытается установить соединение с сервером WWW. Адрес сервера WWW передается методу GetHttpConnection класса CInternetSession. Если соединение с сервером будет установлено, то этот метод вернет указатель на объект класса GetHttpConnection и мы запишем его во временную переменную m_HttpConnection:

// Определяем указатель на объект класса CHttpConnection

CHttpConnection*  m_HttpConnection = NULL;



// Пытаемся соединиться с сервером sServer

m_HttpConnection =

   m_InternetSession -> GetHttpConnection( sServer );

Далее приложение HttpConnection вызывает для установленного соединения метод OpenRequest. Первый параметр метода OpenRequest определяет действие. В данном случае в качестве этого параметра используется константа HTTP_VERB_HEAD, означающая что мы желаем только получить информацию о объекте указанном во втором параметре метода sObject:

CHttpFile* pFile=NULL;  

. . .

pFile = m_HttpConnection ->

   OpenRequest( CHttpConnection::HTTP_VERB_HEAD,

                sObject

              );

Если метод OpenRequest завершился успешно, он возвращает указатель на объект класса CHttpFile. Далее вы можете использовать методы класса CHttpFile чтобы узнать данные полученные с сервера. Если вам надо получить с сервера файл, вы должны использовать вместо команды HTTP_VERB_HEAD команду HTTP_VERB_GET.

Сначала для объекта pFile вызывается метод AddRequestHeaders. Он добавляет дополнительный заголовок headerInfo к запросу HTTP. Сам заголовок headerInfo содержит текстовую строку, в которой определяются тип файлов, которые может принимать наше приложение и его имя:

CString headerInfo(

    _T("Accept: text/*\r\nUser-Agent: Console Http\r\n"));

if(pFile->AddRequestHeaders(headerInfo))

{

}

Если метод AddRequestHeaders завершился без ошибок, он возвращает ненулевое значение. В этом случае наше приложение вызывает метод SendRequest, который и передает сформированный запрос серверу WWW. Если запрос передан успешно, то метод SendRequest возвращает ненулевое значение. В противном случае возвращается нуль. В случае возникновения ошибок метод SendRequest может вызвать исключение CInternetException:

if(pFile->SendRequest())

{

}

Обратите внимание, что исключение CInternetException может также быть вызвано методом GetHttpConnection. Мы поместили эти методы в один блок try, поэтому исключение вызванное любым из них будет обработано одним и тем же блоком catch.



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

Если метод QueryInfoStatusCode отработал успешно, он возвращает ненулевое значение. В этом случае мы преобразуем значение dwReturnCode в текстовую форму и отображаем его на экране:

DWORD dwReturnCode;

if(pFile -> QueryInfoStatusCode(dwReturnCode))

{

   CString sMessage;

   sMessage.Format("%d",dwReturnCode);

   cout << "SendRequest: " << sMessage << endl << endl;

}

Больше приложение ConsoleHttp ничего не делает с сервером, поэтому закрываем объект pFile и удаляем его из памяти:

pFile->Close();

if(pFile)

   delete pFile;

Затем закрываем соединение с сервером и удаляем соответствующий объект m_HttpConnection из памяти (этот объект создается методом GetHttpConnection):

// Закрываем соединение с сервером WWW

m_HttpConnection -> Close();

// Удаляем объект m_HttpConnection

delete( m_HttpConnection );

Перед окончанием работы приложения, завершаем сеанс связи, вызывая метод Close класса CInternetSession, а затем удаляем соответствующий объект из памяти, вызывая функцию delete:

// Завершаем сеанс связи

m_InternetSession -> Close();

// Удаляем объект m_InternetSession

delete(m_InternetSession);


Весной 1997 года фирма Microsoft


Весной 1997 года фирма Microsoft выпустила новую версию Visual C++ 5.0, которая имеет три редакции - Lerning, Professional и Enterprise.

Значительно изменился внешний вид оболочки Microsoft Visual C++. Меню и панели управления теперь сделаны в стиле навигатора Microsoft Internet Explorer.

В Visual C++ версии 5.0 Microsoft полностью заменила систему подсказки. Теперь для просмотра справочной базы данных используется встроенный навигатор Microsoft Internet Explorer. Соответственно изменился и формат файлов справочной базы данных. Теперь они имеют расширения IVI и IVT и представляют из себя компрессованые и собранные вместе файлы HTML, файлы изображений и исходные тексты примеров.

В Visual C++ 5.0 существенно расширены возможности использования технологии ActiveX. В дополнение к библиотеке MFC в состав Visual C++ включена новая библиотека классов - Active Template Library (ATL).

Библиотека ATL ориентирована на создание небольших по размеру и быстрых в исполнении органов управления ActiveX. Эти свойства играют особенно важную роль в среде Internet, критичной к объему передаваемой информации. Для облегчения работы с библиотекой ATL Visual C++ добавлены новые средства автоматизированной разработки приложений - “волшебник” ATL Object Wizard и ATL Application Wizard.

Существенно доработан компилятор Visual C++, который теперь создает более компактный и быстрый программный код. Появилась возможность разработки приложений, ориентированных на новейшие процессоры фирмы Intel - Pentium Pro и Pentium MMX с расширенным набором мультимедийных команд.

В Visual C++ 5.0 также реализована более полная поддержка стандарта ANSI языка программирования C++. Так, например, появился логический тип bool. Переменные этого типа могут принимать всего два значения true и false.

Версия Visual C++ 5.0 Enterprise Edition содержит дополнительные средства для работы с базами данных.

Претерпели изменения средства галереи компонент Visual C++. Теперь, чтобы добавить в проект новый компонент, вы должны выбрать из меню Project строку Add to Project, а затем из открывшегося временного меню строку Components and Controls. На экране появится диалоговая панель Components and Controls Gallery (галерея компонент и органов управления). Раньше эта диалоговая панель носила название Components Gallery (галерея компонент).

Сразу после установки Microsoft Visual C++ в списке компонент каталога Gallery расположены две папки. Папка Developer Studio Components находятся компоненты Microsoft, а в папке Registered ActiveX Controls органы управления ActiveX установленные в системе.


Вспомогательные файлы


Кроме уже описанных нами файлов в проект Look входят еще два исходных файла, содержащих программный код. Это файлы stdafx.cpp и включаемый файл stdafx.h. Исходный текст файла stdafx.cpp содержится в листинге 4.9. Как видите, он состоит из единственной директивы #include, включающей файл stdafx.h (листинг 4.10).

Листинг 4.9. Файл stdafx.cpp

// Включаем файл stdafx.h, определенный в нашем приложении

#include “stdafx.h“

Файл stdafx.h задействует часто используемые включаемые системные файлы - afxwin.h, afxext.h, afxcmn.h и afxdisp.h.  Эти файлы не изменяются, поэтому Microsoft Visual C++ компилирует их только один раз. За счет этого значительно сокращается время, затрачиваемое на повторное построение проекта.

Листинг 4.10. Файл stdafx.h

// Исключает редко используемые определения при обработке

// файлов заголовков

#define VC_EXTRALEAN

 

// Основные компоненты библиотеки MFC

#include <afxwin.h>

// Расширения MFC

#include <afxext.h>

// Будут использоваться органы управления ActiveX

#include <afxdisp.h>

#ifndef _AFX_NO_AFXCMN_SUPPORT

   // Используется для органов управления Windows

   #include <afxcmn.h>   // Common Controls

#endif // _AFX_NO_AFXCMN_SUPPORT

Методы класса CLookDlg и таблица сообщений этого класса определяются в файле LookDlg.cpp. Мы привели полный исходный текст файла LookDlg.cpp в листинге 4.4.



Библиотека системного программиста" мы начали


В 24 и 28 томах серии " Библиотека системного программиста" мы начали рассказывать о программировании приложений Windows с использованием Microsoft Visual C++ и библиотеки классов MFC. Вы узнали об основных принципах построения приложений MFC, использовании диалоговых панелей, меню, панелях управления и панелях состояния, получили общее представление о работе с базами данных.
В этом томе мы продолжим изучение библиотеки классов MFC и ее возможностей. Теперь мы расскажем о использовании Microsoft Visual C++ и библиотеки MFC для разработки приложений Internet и Intranet. Мы также затронем технологию ActiveX и расскажем о том как использовать в своих приложениях готовые органы управления OLE и ActiveX. В приложениях, представленных в данной книге, мы будем активно работать с диалоговыми панелями. На примере списка с пиктограммами и линейного индикатора вы научитесь использовать стандартные органы управления Windows 95 и Windows NT. Мы также приведем начальные сведения о использовании мультизадачности в приложениях MFC. Одно из наших приложений будет создавать дополнительную задачу, выполняющую загрузку файлов с сервера FTP в фоновом режиме.
До недавнего времени создание приложений, которые могут работать с ресурсами сети Internet или Intranet, являлось достаточно сложной задачей. Программист должен был использовать интерфейс сокетов Windows или программировать на более низком уровне протокола TCP\IP.
Чтобы облегчить тяжелый труд программиста, Microsoft разработала новый программный интерфейс Win32 Internet, получивший красивое название WinInet. С использованием WinInet значительно облегчается создание приложений, работающих с протоколами HTTP, FTP и системой Gopher. При этом программист избавляется не только от необходимости программирования на уровне сокетов Windows или протокола TCP\IP, но и от самостоятельной реализации протоколов HTTP и FTP.
Фактически WinInet представляет собой набор функций, реализующих протоколы HTTP (Hypertext Transfer Protocol), FTP (File Transfer Protocol), а также включающих средства для работы с системой Gopher.


В состав библиотеки MFC начиная с версии 4.2 включен целый ряд классов, надстроенных над программным интерфейсом WinInet. Мы рассмотрим использование этих классов на примере нескольких приложений, предназначенных для работы с серверами FTP и WWW.
Приложение FtpView, рассмотренное в этой книге, позволяет соединиться с сервером FTP, просмотреть структуру каталогов сервера и даже загрузить с сервера нужные вам файлы. Модифицируя приложение FtpView мы продемонстрируем различные приемы работы с серверами FTP.
Так как загрузка файлов с сервера, особенно большого размера, может занять слишком много времени, мы расскажем о том, как можно организовать получение файла в фоновом режиме с помощью отдельной задачи. Попутно вы узнаете о том, как приложение, построенное с использованием библиотеки классов MFC, может создать несколько задач и организовать их взаимодействие.
В отдельном разделе мы рассмотрим взаимодействие с серверами WWW. На примере приложения Console HTTP мы покажем как с использованием WinInet можно передать серверу WWW запрос и получить от него интересующую вас информацию или текст страниц WWW.
Естественно, даже с использованием классов WinInet, сложно создать полноценное приложение, которое позволяет просматривать страницы WWW. Достаточно того, что такое приложение должно не только правильно интерпретировать все операторы языка HTML, но также исполнять программы на языке JAVA, и правильно отображать органы управления ActiveX.
Вы, однако, можете очень быстро создать настоящий навигатор Internet, если воспользуетесь органом управления Microsoft Web Browser Control. На основе этого органа управления построен навигатор Microsoft Internet Explorer.
Microsoft Web Browser Control представляет собой орган управления ActiveX, который вы можете включить в свое приложение и использовать его как другие органы управления Windows.
Мы приведем исходный текст приложения Look, использует Microsoft Web Browser Control для просмотра серверов Internet. Наше приложение представляет собой маленький навигатор Internet Explorer, который обладает практически теми же возможностями, что и настоящий навигатор Microsoft Internet Explorer.
В заключение мы расскажем о том, как ваше приложение может использовать Microsoft Internet Explorer, Netscape Navigator и другие навигаторы для просмотра ресурсов сети Internet или Intranet. Таким образом, например, можно организовать получение из Internet самой последней справочной информации по вашему приложению.

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


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

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



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


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



Загрузка файла в фоновом режиме


Исходный вариант приложения FtpView и его модификация обладают еще одним очень неприятным недостатком. Во время загрузки файлов с сервера FTP интерфейс приложения блокируется практически полностью. Только по линейному индикатору, который используется в доработанном варианте  приложения мы можем судить о том, что оно все же работает.

В остальном складывается такое впечатление, что приложение “зависло”. Оно даже не перерисовывает свое окно и если временно переключится на другое приложение, окно которого перекроет окно FtpView, то изображение в окне FtpView будет испорчено. Только по окончании загрузки файла наше приложение спохватится и восстановит свое окно.

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

Сейчас мы предлагаем вам испробовать второй путь и создать для загрузки файлов с сервера отдельную задачу, которая будет выполняться параллельно с основной задачей приложения. Таким образом, загрузка файла будет осуществляться в фоновом режиме и не будет блокировать пользовательский интерфейс приложения.

За основу мы взяли базовый вариант приложения FtpView, выполняющий загрузку файлов с сервера FTP при помощи метода GetFile. Мы постарались свести все изменения в исходных текстах приложения к минимуму. Вы должны будете только добавить три глобальные переменные, изменить метод FtpFileDownload класса CFtpViewDlg, и добавить функцию ThreadFileLoadProc.

Определение глобальных переменных добавьте в самом начале файла FtpViewDlg.cpp непосредственно после директив препроцессора. Все три новых глобальных переменных global_sFtpAddress, global_sFileName и global_sLocalFileName являются объектами класса CString. Эти переменные используются для передачи задаче, осуществляющей загрузку файла, адреса сервера FTP, пути каталога и имени исходного файла на сервере FTP, а также полного пути файла на диске локального компьютера в который будет записан файл с сервера:


//================== Глобальные переменные ===================

// Адрес сервера FTP

CString global_sFtpAddress = "";   

// Полный путь файла на диске локального компьютера

CString global_sFileName = "";     

// Путь каталога и имя файла на сервере FTP

CString global_sLocalFileName = "";

Задача, осуществляющая загрузку файла с сервера FTP, определяется функцией ThreadFileLoadProc. Как видите, мы не стали ее особенно усложнять и просто создали еще один сеанс связи с Internet, а затем в его рамках осуществили соединение с сервером FTP и получили с него заданный файл.

Адрес сервера FTP мы берем из глобальной переменной global_sFtpAddress, путь файла, который надо загрузить с сервера и его название - из переменной global_sFileName, а полный путь файла на диске локального компьютера, куда полученный файл должен быть записан - из переменной global_sLocalFileName:

//============================================================

// Задача, выполняющая загрузку файла с сервера FTP

//============================================================

UINT ThreadFileLoadProc(LPVOID param)

{

   //=========================================================

   // Инициализируем сеанс связи с Internet

   //=========================================================

   CFtpConnection*   m_FtpConnection;   // Сервер FTP

   CInternetSession* m_InternetSession; // Сеанс связи

   // Создаем сеанс связи с Internet, указываем в качестве

   // имени программы-клиента строку FtpProcess

   m_InternetSession = new CInternetSession("FtpProcess");

   // В случае ошибки отображаем сообщение и завершаем

   // задачу с кодом завершения 1

   if(!m_InternetSession)

   {

      AfxMessageBox("New Session Error", MB_OK);

      return 1;

   }

  

   // Инициализируем указатель m_FtpConnection

   m_FtpConnection = NULL;

   // Пытаемся соединиться с сервером FTP

   try

   {

      // Соединяемся с сервером FTP. Эта операция



      // может вызвать исключение CInternetException

      m_FtpConnection = m_InternetSession ->

         GetFtpConnection(global_sFtpAddress);

   }

   catch (CInternetException* pEx)

   {

      // Выводим сообщение об ошибке

      AfxMessageBox("GetFtpConnection Error");

      // Удаляем исключение

      pEx->Delete();

      // Обнуляем указатель m_FtpConnection

      m_FtpConnection = NULL;

   }

   if(m_FtpConnection != NULL)

   {

      BOOL fResult;

      // Загружаем файл с сервера

      fResult = m_FtpConnection ->

         GetFile(global_sFileName.GetBuffer(MIN_LEN_BUF),

                 global_sLocalFileName.GetBuffer(MIN_LEN_BUF),

                 FALSE

         );

      // Закрываем соединение с сервером

      m_FtpConnection -> Close();

      delete m_FtpConnection;

      // После успешного завершения загрузки выводим сообщение

      if( fResult )

         AfxMessageBox("File load complited:" +

                        global_sFileName);

   }

   // Завершаем сеанс связи с Internet

   m_InternetSession -> Close();

   delete m_InternetSession;

   return 0;

}

Функция ThreadFileLoadProc выполняет загрузку файла с сервера FTP в файл на диске локального компьютера. Надо только заполнить глобальные переменные global_sFtpAddress, global_sFileName и global_sLocalFileName, а затем запустить функцию ThreadFileLoadProc как отдельную задачу. Для этого измените метод FtpFileDownload класса CFtpViewDlg как это показано ниже:

BOOL CFtpViewDlg::FtpFileDownload( CString sFileName )

{

   BOOL fResult;

   CString sLocalFileName;

   // Определяем объект класса CFileDialog, представляющий

   // стандартную диалоговую панель Save As, в которой

   // по умолчанию выбрано имя файла sFileName

   CFileDialog mFileOpen(FALSE, NULL, sFileName);

   // Отображаем диалоговую панель Open и позволяем

   // пользователю выбрать с помощью нее один файл



   int result = mFileOpen.DoModal();

   // Проверяем как была закрыта диалоговая панель Open -

   // по нажатию кнопки OK или Cancel

   if(result == IDCANCEL)

   {

      // Если пользователь отказался от выбора файлов и

      // нажал кнопку Cancel отображаем соответствующее

      // сообщение и возвращаем значение FALSE

      AfxMessageBox("File not selected");

      return FALSE;

   }

   // Если пользователь нажал кнопку OK

   else if(result == IDOK)

   {

      // Записываем полный путь файла на диске локального

      // компьютера

      global_sLocalFileName = mFileOpen.GetPathName();

      // Определяем путь и имя файла, который надо загрузить

      // с сервера FTP

      global_sFileName = sCurentDirectory + "/" + sFileName;

      // Запоминаем адрес сервера FTP

      global_sFtpAddress = m_FtpAddress;

      // Запускаем новую задачу, определенную функцией

      // ThreadFileLoadProc

      AfxBeginThread( ThreadFileLoadProc, NULL,

                      THREAD_PRIORITY_NORMAL);

   }

   return fResult;

}

Мы не будем подробно описывать функцию ThreadFileLoadProc и новый вариант метода FtpFileDownload класса CFtpViewDlg. В них используются те же приемы, что продемонстрированы в методе FtpFileDownload класса CFtpViewDlg базового варианта приложения FtpView.


Загрузка файлов с сервера FTP


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

Приложение FtpView построено таким образом, что при выборе из списка файла, метод OnDblclkFtpList отображает на экране предупреждающее сообщение и больше ничего не делает. Добавим к классу CFtpViewDlg новый метод FtpFileDownLoad, которое будет выполнять загрузку файла с данным именем. Тогда нам останется немного исправить метод OnDblclkFtpList и приложение для загрузки файлов готово.



Запустите приложение


Если компьютер настроен для работы с Internet, но в настоящий момент соединение не установлено, подключитесь к Internet. Запустите приложение Look (рис. 4.12). Сразу после того, как на экране появится панель приложения Look, вы можете перейти к просмотру серверов WWW и FTP. Для этого выберите адрес сервера из списка Address или введите его вручную. Нажмите кнопку Navigate. Через некоторое время в окне просмотра появится выбранная вами страница. В зависимости от множества причин, таких как скорость соединения вашего компьютера с сервером поставщика услуг Internet, загруженности его сервера и сервера, с которым вы соединяетесь, возможны значительные задержки при загрузке страниц из сети.

Когда страничка WWW или FTP появится в окне приложения, вы можете перейти к просмотру других страниц, используя приложение Look как действующую модель Internet Explorer в натуральную величину.

Рис. 4.12. Приложение Look