Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

© Александр Фролов, Григорий Фролов
Том 31, М.: Диалог-МИФИ, 1997, 286 стр.

[Назад] [Содеожание] [Дальше]

Приложение 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 томе серии “Библиотека системного программиста” в разделе “Приложение с главной диалоговой панелью”.

Ресурсы приложения 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

Исходные тексты приложения FtpView

Когда вы закончите создание и доработку ресурсов приложения FtpView, надо приступить к доработке программных кодов приложения. Список файлов, составляющих проект FtpView, вы можете просмотреть в окне Project Workspace на странице FileView (рис. 2.7). Вы можете непосредственно внести все необходимые изменения в исходные файлы проекта, однако значительно лучше переложить часть работы на MFC ClassWizard. Средствами MFC ClassWizard вы сможете легко добавить к классам новые элементы данных и новые методы.

Рис. 2.7. Файлы проекта FtpView

Класс CFtpViewApp

Главный класс приложения CFtpViewApp, определен в файле FtpView.h. Мы привели исходный текст этого файла в листинге 2.4. Класс CFtpViewApp наследуется от базового класса CWinApp. При этом переопределяется единственный виртуальный метод InitInstance XE "CWinApp:InitInstance" , который выполняет инициализацию приложения и отображает на экране главную диалоговою панель приложения.

Листинг 2.4. Файл FtpView.h


#ifndef __AFXWIN_H__
  #error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"   // Включаемый файл содержащий 
                        // Идентификаторы ресурсов приложения

//============================================================
// Определение класса CFtpViewApp
// Методы класса CDialogApp определены в файле FtpView.cpp
//============================================================
class CFtpViewApp : public CWinApp
{
public:
   CFtpViewApp();

// Overrides
   // В следующем блоке ClassWizard помещает описания 
   // переопределенных виртуальных методов класса
   //{{AFX_VIRTUAL(CFtpViewApp)
   public:
   virtual BOOL InitInstance();
   //}}AFX_VIRTUAL

// Implementation

   //{{AFX_MSG(CFtpViewApp)
   // В этом блоке ClassWizard размещает описания методов 
   // класса. Не редактируйте содержимое этого блока вручную
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

Определение конструктора класса CFtpViewApp, метода InitInstance, таблицы сообщений, а также определение объекта данного класса вы найдете в файле FtpView.cpp. Исходный текст этого файла мы представили в листинге 2.5. Заметим, что файлы FtpView.h и FtpView.cpp мы оставляем без изменения какими их создал MFC AppWizard.

Весь программный код, который будет взаимодействовать с сервером FTP и обслуживать диалоговую панель IDD_FTPVIEW_DIALOG, мы добавим к классу CFtpViewDlg. По большей части мы будем использовать для этого средства MFC ClassWizard, так что вручную вам надо будет ввести только исходный текст добавленных методов.

Листинг 2.5. Файл FtpView.cpp


//============================================================
// Приложение для просмотра структуры каталогов 
// серверов FTP
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
//============================================================
// Файл содержит определение методов и таблицы сообщений 
// главного класса приложения
//============================================================

// Включаемые файлы 
#include "stdafx.h"
#include "FtpView.h"
#include "FtpViewDlg.h"

// Для отладочной версии приложения включается дополнительные 
// определения
#ifdef _DEBUG
   #define new DEBUG_NEW
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
#endif

//============================================================
// Таблица сообщений класса CFtpViewApp
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewApp, CWinApp)
   //{{AFX_MSG_MAP(CFtpViewApp)
   //}}AFX_MSG
   ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

//============================================================
// Конструктор класса CFtpViewApp
//============================================================
CFtpViewApp::CFtpViewApp()
{
   // TODO: 
}

//////////////////////////////////////////////////////////////
// Объект главного класса приложения
CFtpViewApp theApp;

//============================================================
// Метод InitInstance класса CFtpViewApp.
// Выполняет инициализацию приложения
//============================================================
BOOL CFtpViewApp::InitInstance()
{
#ifdef _AFXDLL
   Enable3dControls(); 
#else
   Enable3dControlsStatic(); 
#endif

   CFtpViewDlg dlg;
   m_pMainWnd = &dlg;
   int nResponse = dlg.DoModal();
   if (nResponse == IDOK)
   {
      // TODO: Нажата кнопка OK
   }
   else if (nResponse == IDCANCEL)
   {
      // TODO: Нажата кнопка Cancel
   }

   // Так как диалоговая панель закрыта, возвращаем значение 
   // FALSE чтобы завершить приложение 
   return FALSE;
}

Класс CFtpViewDlg

Все изменения, которые мы будем вносить в наш проект затронут, в основном, только класса CFtpViewDlg. Это и понятно - именно к диалоговой панели IDD_FTPVIEW_DIALOG, которая управляется данным классом, мы добавили новые органы управления для взаимодействия с сервером FTP.

Приложения, которые используют для доступа к ресурсам сети Internet классы MFC WinInet, должны иметь как минимум один объект класса CInternetSession. Этот объект представляет собой “сеанс” связи с Internet.

В окне Project Workspace щелкните правой клавишей мыши по имени класса CFtpViewDlg. Откроется контекстное меню, из которого следует выбрать строку Add Variable. На экране появится диалоговая панель Add Member Variable (рис. 2.8).

Рис. 2.8. Диалоговая панель Add Member Variable

В поле Variable Type введите имя класса CInternetSession, а в поле Variable Declaration имя нового элемента класса CFtpViewDlg - m_InternetSession. Переключатель Access переведите в положение Protect. Нажмите кнопку OK. В класс CFtpViewDlg будет добавлен новый элемент - объект класса CInternetSession. Так как мы выбрали тип доступа Protect, то этот элемент будет расположен в секции protected и будет доступен только методам данного класса.

Повторите описанную процедуру и добавьте к классу CFtpViewDlg еще три элемента - строку sCurentDirectory класса CString, указатель m_ImageList на класс CImageList и указатель m_FtpConnection на класс CFtpConnection.

С помощью MFC ClassWizard привяжите переменные к органам управления диалоговой панели IDD_FTPVIEW_DIALOG. Для этого откройте MFC ClassWizard, перейдите на страницу Member Variables. В поле Class name выберите имя класса CFtpViewDlg. Затем последовательно выбирайте идентификаторы органов управления из списка Control IDs, нажимайте кнопку Add Variable и в открывающейся панели Add Member Variable вводите имя, категорию и тип переменной.

Добавляя переменные, пользуйтесь рисунком 2.9. На нем видно, какие переменные привязаны к органам управления диалоговой панели. Только кнопка OK не имеет собственной переменной, так как она нам не понадобится.

Рис. 2.9. Диалоговая панель MFC ClassWizard

Перейдите в диалоговой панели MFC ClassWizard на страницу Message Maps и добавьте несколько методов-обработчиков сообщений от органов управления диалоговой панели IDD_FTPVIEW_DIALOG. Для кнопки Connect, имеющей идентификатор IDC_CONNECT, и кнопки On Top с идентификатором IDC_ON_TOP добавьте обработчики командного сообщения BN_CLICKED - методы OnConnect и OnOnTop. Они будут вызываться при нажатии на эти кнопки. Для списка IDC_FTP_LIST добавьте обработчик сообщения NM_DBLCLK - метод OnDblclkFtpList. Он будет вызываться при двойном щелчке левой кнопкой мыши внутри списка.

Теперь остается совсем немного. Надо добавить метод DirectoryView и деструктор к классу CFtpViewDlg. MFC ClassWizard в этом вам не помощник. Откройте окно Project Workspase, если оно закрыто, и щелкните правой клавишей мыши по названию класса CFtpViewDlg. Откроется контекстное меню, из которого надо выбрать строку Add Function. На экране появится диалоговая панель Add Member Function (рис. 2.10).

Рис. 2.10. Диалоговая панель Add Member Function

Чтобы добавить метод DirectoryView, введите в поле Function Declaration его название, а в поле Function Type - тип значения, возвращаемого этим методом - BOOL. Повторите описанную операцию еще один раз и добавьте к классу CFtpViewDlg деструктор ~CFtpViewDlg. Для этого введите в поле Function Declaration строку ~CFtpViewDlg, а поле Function Type оставьте пустым.

MFC ClassWizard создает только шаблоны методов. Рабочий программный код вы должны добавить в них самостоятельно. Ниже мы привели исходные тексты файлов FtpViewDlg.h и FtpViewDlg.cpp, в соответствии с которыми вам надо доработать свой проект.

Класс CFtpViewDlg определен в файле FtpViewDlg.h. Мы привели исходный текст этого файла в листинге 2.6. Для этого файла вам надо добавить директивы define, определяющие четыре константы - COL_NUM, MIN_LEN_BUF, DIRECTORY и FILE. Эти константы будут использоваться методами класса FtpViewDlg.

Листинг 2.6. Файл FtpViewDlg.h


//============================================================
// Определение класса FtpViewDlg и некоторых констант
//============================================================

// Определение констант
#define COL_NUM      4    // Количество колонок таблицы
#define MIN_LEN_BUF  30   // Минимальная длина буфера
#define DIRECTORY    0    // Пиктограмма каталога
#define FILE         1    // Пиктограмма файла

//////////////////////////////////////////////////////////////
// Класс CFtpViewDlg 

class CFtpViewDlg : public CDialog
{
// Construction
public:
   ~CFtpViewDlg();
   CFtpViewDlg(CWnd* pParent = NULL); 

// Dialog Data
   //{{AFX_DATA(CFtpViewDlg)
   enum { IDD = IDD_FTPVIEW_DIALOG };

   CEdit       m_Status;     // Поле Directory
   CButton     m_Ok;         // Кнопка OK
   CButton     m_OnTop;      // Кнопка On top
   CButton     m_Connect;    // Кнопка Connect
   CListCtrl   m_FtpList;    // Таблица с содержимым каталога
   CString     m_FtpAddress; // Поле адреса сервера FTP
   //}}AFX_DATA

   //{{AFX_VIRTUAL(CFtpViewDlg)
   protected:
   // Обмен данными - DDX/DDV 
   virtual void DoDataExchange(CDataExchange* pDX);   
   //}}AFX_VIRTUAL

// Implementation
protected:
   CString sCurentDirectory;            // Текущий каталог
   CImageList* m_ImageList;             // Список изображений
   CFtpConnection* m_FtpConnection;     // Сервер FTP 
   CInternetSession* m_InternetSession; // Сеанс связи 
   HICON m_hIcon;                       // Пиктограмма 
                                        // приложения

protected:
   // Метод, просматривающий содержимое каталога
   BOOL DirectoryView();             

   // Методы обрабатывающие сообщения
   //{{AFX_MSG(CFtpViewDlg)
   virtual BOOL OnInitDialog();
   afx_msg void OnPaint();
   afx_msg HCURSOR OnQueryDragIcon();
   afx_msg void OnOnTop();
   afx_msg void OnDblclkFtpList(NMHDR* pNMHDR, 
      LRESULT* pResult);
   afx_msg void OnConnect();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

¨     В листинге файла FtpViewDlg.h, в блоке AFX_MSG, описание метода OnDblclkFtpList разбито на две строки. Это сделано исключительно из-за ограничений, налагаемых типографией. Вы должны поместить данное описание на одной строке, в противном случае MFC ClassWizard будет выдавать сообщение об ошибке и вы не сможете воспользоваться его средствами для просмотра и изменения программного кода

Конструктор, деструктор, методы класса CFtpViewDlg, а также таблица сообщений определены в файле FtpViewDlg.cpp. Мы привели исходный текст файла FtpViewDlg.cpp в листинге 2.7. В соответствии с текстом этого файла вы должны доработать методы OnInitDialog, OnConnect, DirectoryView, OnDblclkFtpList, OnOnTop.

Листинг 2.7. Файл FtpViewDlg.cpp


#include "stdafx.h"
#include "FtpView.h"
#include "FtpViewDlg.h"

#ifdef _DEBUG
   #define new DEBUG_NEW
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
#endif

//============================================================
// Конструктор класса CFtpViewDlg 
//============================================================
CFtpViewDlg::CFtpViewDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CFtpViewDlg::IDD, pParent)
{
   //{{AFX_DATA_INIT(CFtpViewDlg)
   m_FtpAddress = _T("");
   //}}AFX_DATA_INIT

   // При использовании метода LoadIcon нет необходимости 
   // вызывать DestroyIcon 
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

//============================================================
// Деструктор класса CFtpViewDlg 
//============================================================
CFtpViewDlg::~CFtpViewDlg()
{
   // Если соединение с сервером установлено, закрываем его
   if (m_FtpConnection != NULL)
   {
      m_FtpConnection -> Close();
      delete m_FtpConnection;
   }
   // Завершаем сеанс связи с Internet
   if (m_InternetSession != NULL)
   {
      m_InternetSession -> Close();
      delete m_InternetSession;
   }
   // Удаляем список изображений
   delete m_ImageList;
}

//============================================================
// Метод DoDataExchange класса CFtpViewDlg
// Выполняет привязку органов управления диалоговой панели 
// IDD_FTPVIEW_DIALOG и соответствующих элементов класса 
// CFtpViewDlg
//============================================================
void CFtpViewDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CFtpViewDlg)
   DDX_Control(pDX, IDC_STATUS, m_Status);
   DDX_Control(pDX, IDOK, m_Ok);
   DDX_Control(pDX, IDC_ON_TOP, m_OnTop);
   DDX_Control(pDX, IDC_CONNECT, m_Connect);
   DDX_Control(pDX, IDC_FTP_LIST, m_FtpList);
   DDX_Text(pDX, IDC_FTP_ADDRESS, m_FtpAddress);
   //}}AFX_DATA_MAP
}

//============================================================
// Таблица сообщений класса CFtpViewDlg 
//============================================================
BEGIN_MESSAGE_MAP(CFtpViewDlg, CDialog)
   //{{AFX_MSG_MAP(CFtpViewDlg)
   ON_WM_PAINT()
   ON_WM_QUERYDRAGICON()

   // Сообщение от кнопки Connect
   ON_BN_CLICKED(IDC_CONNECT, OnConnect)
   // Сообщение от кнопки On Top
   ON_BN_CLICKED(IDC_ON_TOP, OnOnTop)
   // Сообщение с кодом извещения NM_DBLCLK от списка
   ON_NOTIFY(NM_DBLCLK, IDC_FTP_LIST, OnDblclkFtpList)
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

//============================================================
// Метод OnInitDialog класса CFtpViewDlg 
// Выполняет инициализацию диалоговой панели, а также сеанса
// связи с Internet (создает объект класса CInternetSession)
//============================================================
BOOL CFtpViewDlg::OnInitDialog()
{
   // Вызываем метод OnInitDialog базового класса CDialog
   CDialog::OnInitDialog();

   // Устанавливаем пиктограммы, которые будут отображаться 
   // в случае минимизации диалоговой панели 
   SetIcon(m_hIcon,TRUE);  // Пиктограмма стандартного размера
   SetIcon(m_hIcon,FALSE); // Пиктограмма маленького размера
   
   //=========================================================
   // Выполняем инициализацию списка IDC_FTP_LIST
   //=========================================================

   // Структура, для описания характеристик колонок списка
   LV_COLUMN   lv_column;
   // Переменная для определения размера списка
   CRect       rectList;

   // Названия для колонок списка
   TCHAR szColHeader[COL_NUM][10] = { 
         _T("Name"), 
         _T("Length"), 
         _T("Date"),
         _T("Time"), 
   };

   // Определяем размер области, которую занимает список 
   // IDC_FTP_LIST в диалоговой панели 
   m_FtpList.GetWindowRect(&rectList);

   // Указываем поля структуры lv_column, которые будут 
   // использоваться
   lv_column.mask = LVCF_FMT |   // Используется поле fmt
                LVCF_SUBITEM |   // Используется поле iSubItem
                   LVCF_TEXT |   // Используется поле pszText
                  LVCF_WIDTH;    // Используется поле cx

   // Задаем выравнивание по левому краю
   lv_column.fmt = LVCFMT_LEFT;

   // Ширина колонки
   lv_column.cx = (rectList.Width() / COL_NUM) - 1; 

   // Определяем характеристики колонок списка 
   for (int i = 0; i < COL_NUM; i++)  
   {
      // Номер колонки
      lv_column.iSubItem = i;
      // Заголовок колонки 
      lv_column.pszText = szColHeader[i];
      // Добавляем колонку с заданными свойствами к списку
      m_FtpList.InsertColumn(i, &lv_column);
   }

   // Создаем список из двух изображений размера 16 х 16
   m_ImageList = new CImageList();
   m_ImageList -> Create(16, 16, TRUE, 2, 2);

   // Добавляем в список две пиктограммы - 
   // IDI_DIRECTORY (изображение каталога) и 
   // IDI_FILE(изображение файла ) 
   m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_DIRECTORY));
   m_ImageList -> Add(AfxGetApp()->LoadIcon(IDI_FILE));

   // Выбираем список изображений m_ImageList для 
   // использования в списке IDC_FTP_LIST
   m_FtpList.SetImageList(m_ImageList, LVSIL_SMALL);

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

   m_FtpAddress = "dials.ccas.ru"; // Сервер FTP "ДиалогНаука"
   // Отображаем адрес на экране
   UpdateData(FALSE);

   //=========================================================
   // Инициализируем сеанс связи с Internet
   //=========================================================

   // Создаем сеанс связи с Internet, указываем в качестве 
   // имени программы-клиента название приложения FtpView
   m_InternetSession = new CInternetSession("FtpView");

   // В случае ошибки отображаем сообщение и завершаем 
   // приложение
   if(!m_InternetSession)
   {
      AfxMessageBox("New Session Error", MB_OK);
      OnOK();
   }
   // Инициализируем указатель m_FtpConnection
   m_FtpConnection = NULL;

   return TRUE;
}

//============================================================
// Метод OnPaint класса CFtpViewDlg 
// Отображает на экране пиктограмму в случае минимизации
// главной диалоговой панели приложения
//============================================================
void CFtpViewDlg::OnPaint() 
{
   if (IsIconic())
   {
      CPaintDC dc(this); 

      SendMessage(WM_ICONERASEBKGND, 
         (WPARAM) dc.GetSafeHdc(), 0);

      int cxIcon = GetSystemMetrics(SM_CXICON);
      int cyIcon = GetSystemMetrics(SM_CYICON);

      CRect rect;
      GetClientRect(&rect);
      
      int x = (rect.Width() - cxIcon + 1) / 2;
      int y = (rect.Height() - cyIcon + 1) / 2;

      dc.DrawIcon(x, y, m_hIcon);
   }
   else
   {
      CDialog::OnPaint();
   }
}

//============================================================
// Метод OnQueryDragIcon класса CFtpViewDlg 
// Сообщает идентификатор пиктограммы, отображаемой в случае 
// минимизации приложения
//============================================================
HCURSOR CFtpViewDlg::OnQueryDragIcon()
{
   return (HCURSOR) m_hIcon;
}

//============================================================
// Метод OnOnTop класса CFtpViewDlg 
// Переходит в каталог верхнего уровня
//============================================================
void CFtpViewDlg::OnOnTop() 
{
   // Блокируем список IDC_FTP_LIST
   m_FtpList.EnableWindow(FALSE);

   // Изменяем строку текущего каталога sCurentDirectory так, 
   // чтобы она показывала на каталог верхнего уровня.
   // Ищем последнее вхождение символа / в строку с именем 
   // каталога 
   int iNum = sCurentDirectory.ReverseFind('/');
   if(iNum == -1)
   {
      // Если символ / не обнаружен, значит мы находимся в 
      // корневом каталоге
      AfxMessageBox("No top directory");
   }
   else
   {
      // Удаляем из строки с именем текущего каталога названия 
      // последнего каталога
      sCurentDirectory = sCurentDirectory.Left(iNum);

      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   
   
      // Отображаем содержимое каталога верхнего уровня
      DirectoryView();   
   }

   // Снимаем блокировку списка IDC_FTP_LIST
   m_FtpList.EnableWindow(TRUE);

   // Отображаем на диалоговой панели новый путь каталога
   m_Status.SetWindowText(sCurentDirectory);

   return;
}

//============================================================
// Метод OnDblclkFtpList класса CFtpViewDlg 
// Переходит в выбранный каталог
//============================================================
void CFtpViewDlg::OnDblclkFtpList(NMHDR* pNMHDR, 
   LRESULT* pResult) 
{
   int       iTotalNumber; // Количество элементов списка
   CString   sSelItem;     // Название каталога
   CString   sLength_Dir;  // Длина файла или строка Dir

   // Блокируем список IDC_FTP_LIST
   m_FtpList.EnableWindow(FALSE);

   // Определяем количество элементов в списке IDC_FTP_LIST
   iTotalNumber = m_FtpList.GetItemCount();

   // Определяем, какой объект списка выбран
   for(int i = 0; i < iTotalNumber; i++)
   {
      // Атрибут LVIS_SELECTED установлен
      // у выбранного элемента списка 
      if(LVIS_SELECTED == m_FtpList.GetItemState( i, 
         LVIS_SELECTED ))
      {
         // Определяем название выбранного элемента списка
         // (имя файла или каталога)
         sSelItem = m_FtpList.GetItemText( i, 0 );

         // Считываем данные из колонки Length
         sLength_Dir = m_FtpList.GetItemText( i, 1 );

         if(sLength_Dir == "Dir") // Выбран каталог
            // Переходим в выбранный каталог
            sCurentDirectory  = 
               sCurentDirectory + "/" + sSelItem;

         else                     // Выбран файл
            // Отображаем имя файла
            AfxMessageBox("You select file " + sSelItem);
         
         break;
      }
   }
   
   // Меняем форму курсора (курсор "ожидание")
   CWaitCursor wait;   

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

   // Отображаем в диалоговой панели новый путь каталога
   m_Status.SetWindowText(sCurentDirectory);

   // Снимаем блокировку списка IDC_FTP_LIST
   m_FtpList.EnableWindow(TRUE);

   *pResult = 0;
}

//============================================================
// Метод OnConnect класса CFtpViewDlg 
// Соединяется с указанным сервером FTP
//============================================================
void CFtpViewDlg::OnConnect() 
{
   // Текущий каталог на сервере FTP
   CString  sCurrentFtpDirectory = "";

   // Блокируем кнопки Connect, OK и On Top
   m_Connect.EnableWindow(FALSE);
   m_Ok.EnableWindow(FALSE);
   m_OnTop.EnableWindow(FALSE);

   // Если вы ранее уже соединились с сервером FTP, разрываем 
   // эту связь и удаляем объект m_FtpConnection
   if (m_FtpConnection != NULL)
   {
      m_FtpConnection -> Close();
      
      delete m_FtpConnection;   
      m_FtpConnection = NULL;
   }

   // Считываем из диалоговой панели адрес сервера FTP, так 
   // как пользователь уже мог его изменить
   UpdateData(TRUE);

   // Пытаемся соединиться с сервером FTP 
   try
   {
      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   

      // Соединяемся с сервером FTP. Эта операция
      // может вызвать исключение CInternetException
      m_FtpConnection = 
         m_InternetSession->GetFtpConnection(m_FtpAddress);
   }
   catch (CInternetException* pEx)
   {
      // Обрабатываем исключение CInternetException
      TCHAR szErr[1024];  // временный буфер для сообщения

      // Выводим сообщение об ошибке
      if (pEx->GetErrorMessage(szErr, 1024))
         AfxMessageBox(szErr);
      else
         AfxMessageBox("GetFtpConnection Error");

      // Удаляем исключение
      pEx->Delete();

      // Обнуляем указатель m_FtpConnection
      m_FtpConnection = NULL;
   }

   // Если соединение не установлено сообщяем об этом
   if( m_FtpConnection == NULL )
      m_Status.SetWindowText("Connect not established");

   // Если соединение установлено, определяем текущий каталог 
   // и отображаем его содержимое на экране
   else
   {
      // Определяем текущий каталог сервера FTP 
      BOOL fResult= 
         m_FtpConnection -> 
            GetCurrentDirectory(sCurrentFtpDirectory);
      if(fResult)
         sCurentDirectory = sCurrentFtpDirectory;
      else 
         AfxMessageBox("GetCurrentDirectory Error");

      // Меняем форму курсора (курсор "ожидание")
      CWaitCursor wait;   

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

      // Отображаем на диалоговой панели новый путь каталога
      m_Status.SetWindowText(sCurentDirectory);
   }

   // Снимаем блокировку кнопок Connect, OK и On Top
   m_Connect.EnableWindow(TRUE);
   m_Ok.EnableWindow(TRUE);
   m_OnTop.EnableWindow(TRUE);
}

//============================================================
// Метод DirectoryView класса CFtpViewDlg 
// Просматривает содержимое каталога и отображает его в 
// таблице m_FtpList
//============================================================
BOOL CFtpViewDlg::DirectoryView()
{
   // Переменная, сигнализирующая о получении последнего 
   // элемента каталога
   BOOL     fResult; 

   // Временная переменная, определяющая тип объекта -
   // файл или каталог 
   BOOL     fDirectory;

   // Структура для добавления нового элемента к списку
   LV_ITEM   lv_item;   

   // Удалить все элементы из списка IDC_FTP_LIST
   m_FtpList.DeleteAllItems();

   // Создаем объект класса CFtpFileFind, указывая 
   // объект, представляющий уже установленное соединение
   // с сервером FTP 
   CFtpFileFind m_FtpFileFind(m_FtpConnection);

   // Получаем имена всех объектов текущего каталога
   if(fResult = 
      m_FtpFileFind.FindFile(_T(sCurentDirectory + "/*")))
   {
      for(int n = 0;fResult; n++)
      {
         // Получаем очередной объект из каталога
         fResult = m_FtpFileFind.FindNextFile();

         // Определяем что это - каталог или файл
         fDirectory = m_FtpFileFind.IsDirectory();

         //============= Определяем имя объекта ==============
         // Временные строка для имени каталога или файла
         CString   fileName;

         // Определяем имя объекта
         fileName = m_FtpFileFind.GetFileName();

         // Заполняем структуру lv_item, сведениями об 
         // очередном объекте каталога сервера FTP. Указываем, 
         // что в список добавляется текст и изображение
         lv_item.mask = LVIF_TEXT | LVIF_IMAGE; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем первую колонку
         lv_item.iSubItem = 0;
         // Выбираем изображение для нового элемента списка в 
         // зависимости от типа объекта 
         lv_item.iImage = (fDirectory) ? DIRECTORY : FILE;
         // Указываем имя каталога или файла
         lv_item.pszText = fileName.GetBuffer(MIN_LEN_BUF);

         // Добавляем новый элемент к списку IDC_FTP_LIST
         m_FtpList.InsertItem(&lv_item);

         //============= Определяем длину файла =============
         // Длинна файла
         DWORD dwLength = 0;
         // Временные строка для формирования текстового 
         // представления длинны файла
         CString sLength;

         // Заполняем колонку Length для новой записи и 
         // записываем в нее длинну файла или строку Dir, если 
         // новый объект - каталог.
         // Добавляется только текст без пиктограммы
         lv_item.mask = LVIF_TEXT; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем вторую колонку
         lv_item.iSubItem = 1;
         // Если очередной объект является каталогом, то 
         // вместо в колонке Length записываем строку Dir
         if(fDirectory)
         {
            lv_item.pszText = "Dir";
         }

         // Если очередной объект является файлом, то 
         // записываем его длину в колонку Length 
         else
         {
            // Определяем длину файла
            dwLength = m_FtpFileFind.GetLength();
            // Формируем текстовое представление длины файла
            sLength.Format("%d", dwLength);
            lv_item.pszText = sLength.GetBuffer(MIN_LEN_BUF);
         }
        
         // Добавляем запись во второй колонке списка (колонка 
         // Length)
         m_FtpList.SetItem(&lv_item);

         //======== Определяем дату и время создания =========
         // Дата и время создания каталога или файла
         CTime mTime;  
         // Временные строки для формирования текстового 
         // представления даты и времени 
         CString sDate;
         CString sTime;

         // Определяем время изменения файла или каталога
         if(!m_FtpFileFind.GetLastWriteTime(mTime))
            break;

         // Добавляется только текст без пиктограммы
         lv_item.mask = LVIF_TEXT; 
         // Указываем номер строки в списке
         lv_item.iItem = n;
         // Заполняем третью колонку
         lv_item.iSubItem = 2;
         // Выделяем из объекта mTime день, месяц и год 
         sDate = mTime.Format("%d.%m.%y");
         // Записываем сформированную дату в структуру lv_item
         lv_item.pszText = sDate.GetBuffer(MIN_LEN_BUF);

         // Добавляем запись во второй колонке списка (колонка 
         // Date)
         m_FtpList.SetItem(&lv_item);

         // Заполняем четвертую колонку, записываем в нее 
         // время последнего изменения файла (каталога)
         lv_item.iSubItem = 3;
         // Выделяем из объекта mTime часы, минуты и секунды 
         sTime = mTime.Format("%H:%M:%S");
         // Записываем сформированную строку, содержащую время
         lv_item.pszText = sTime.GetBuffer(MIN_LEN_BUF);

         // Добавляем запись во второй колонке списка (колонка 
         // Time)
         m_FtpList.SetItem(&lv_item);
      }
      // Заканчиваем поиск объектов в каталоге, закрываем 
      // объект m_FtpFileFind
      m_FtpFileFind.Close();
   }
   // Если каталог не содержит других объектов - каталогов и 
   // файлов, выводин соответствующее сообщение
   else
      AfxMessageBox("File's not found or error");

   return TRUE;
}

Кроме уже описанных нами файлов в проект FtpView входят еще два исходных файла, содержащих программный код. Это файлы stdafx.cpp и включаемый файл stdafx.h. Исходный текст файла stdafx.cpp содержится в листинге 2.8. Как видите, он состоит из единственной директивы #include, включающей файл stdafx.h.

Листинг 2.8. Файл stdafx.cpp


// Включаем файл stdafx.h, определенный в нашем приложении
#include "stdafx.h"

Файл stdafx.h задействует часто используемые включаемые системные файлы - afxwin.h, afxext.h, afxcmn.h и afxinet.h. Эти файлы не изменяются. Поэтому Microsoft Visual C++ компилирует их только один раз. За счет этого значительно сокращается время, затрачиваемое на повторное построение проекта.

Последний включаемый файл afxinet.h содержит описание классов, структур и констант библиотеки MFC, относящихся к программному интерфейсу WinInet (листинг 2.9). MFC AppWizard не включает этот файл по умолчанию. Вы должны добавить соответствующую директиву #include самостоятельно.


#include "stdafx.h"

Листинг 2.9. Файл stdafx.h


// Исключает редко используемые определения при обработке 
// файлов заголовков
#define VC_EXTRALEAN 
 
// Основные компоненты библиотеки MFC 
#include <afxwin.h> 

// Расширения MFC
#include <afxext.h> 

#ifndef _AFX_NO_AFXCMN_SUPPORT
   // Используется для органов управления Windows
   #include <afxcmn.h> 
#endif // _AFX_NO_AFXCMN_SUPPORT

// Включаемый файл для библиотеки WinInet.
// MFC AppWizard не включает этот файл, вы должны сделать это
// самостоятельно 
#include "afxinet.h"

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

Как устроено приложение FtpView

Основу приложения FtpView составляют два класса CFtpViewApp и CFtpViewDlg. Класс CFtpViewApp является основным классом приложения. Он выполняет инициализацию и отображает диалоговую панель приложения. Класс CFtpViewDlg управляет этой диалоговой панелью, а также соединяется с Internet для получения списка объектов на сервере FTP.

На рисунке 2.13 представлена страница ClassView окна проекта Project Workspace. На ней вы можете просмотреть структуру классов нашего приложения.

Рис. 2.13. Классы приложения FtpView

Класс CFtpViewApp

Класс CFtpViewApp является главным классом приложения и наследуется от базового класса CWinApp. Этот класс самый маленький. В состав класса CFtpViewApp входят только конструктор и метод InitInstance. Конструктор класса CFtpViewApp не выполняет никаких действий. Он создается MFC AppWizard и мы оставляем его без изменений.

Метод InitInstance также создается MFC AppWizard. Он переопределяет виртуальный метод InitInstance базового класса CWinApp. Виртуальный метод InitInstance выполняет инициализацию приложения и отображает на экране главную диалоговою панель приложения, которая имеет идентификатор IDD_FTPVIEW_DIALOG и управляется классом CFtpViewDlg. Более подробно этот метод описан в 24 томе серии “Библиотека системного программиста”.

Класс CFtpViewDlg

Класс CFtpViewDlg управляет диалоговой панелью приложения и выполняет всю работу по связи с Internet и сервером FTP. Поэтому основное внимание мы уделим именно этому классу.

Кроме конструктора и деструктора в состав класса CFtpViewDlg входит еще восемь методов - OnInitDialog, OnPaint, OnQueryDragIcon, DoDataExchange, OnOnTop, OnDblclkFtpList, OnConnect и DirectoryView. Только первые четыре метода создаются MFC AppWizard по умолчанию, остальные мы добавили с помощью MFC ClassWizard.

Класс CFtpViewDlg также включает несколько элементов данных, предназначенных для управления органами управления диалоговой панели приложения и для установления связи с сервером FTP. Они также были добавлены нами с помощью MFC ClassWizard.

Элементы данных класса CFtpViewDlg

Когда вы с помощью MFC ClassWizard привязывали переменные к органам управления диалоговой панели, в класс были добавлены несколько элементов данных. Все они были занесены с специальный блок, отмеченный комментариями вида //}}AFX_DATA:


//{{AFX_DATA(CFtpViewDlg)
CEdit     m_Status;     // Поле Directory
CButton   m_Ok;         // Кнопка OK
CButton   m_OnTop;      // Кнопка On top
CButton   m_Connect;    // Кнопка Connect
CListCtrl m_FtpList;    // Таблица с содержимым каталога
CString   m_FtpAddress; // Поле адреса сервера FTP
//}}AFX_DATA

Эти элементы данных представляют собой объекты классов MFC, управляющие соответствующими им органами управления диалоговой панели. Например, объект m_Connect класса CButton, привязан к кнопке Connect. Вызывая различные методы для этого объекта вы можете управлять кнопкой Connect (блокировать ее, снимать блокировку и т. д.).

Элементы данных, определенные внутри блока AFX_DATA тесно соотносятся с методом DoDataExchange, который, собственно, и осуществляет их связь с органами управления диалоговой панели.

Другая группа элементов данных класса CFtpViewDlg определена после ключевого слова protected содержит пять объектов. Доступ к этим объектам открыт только для методов самого класса CFtpViewDlg:


protected:
   HICON m_hIcon;                    // Пиктограмма приложения
   CString sCurentDirectory;            // Текущий каталог
   CImageList* m_ImageList;             // Список изображений
   CFtpConnection* m_FtpConnection;     // FTP сервер
   CInternetSession* m_InternetSession; // Сеанс связи 

Элемент m_hIcon используется для хранения идентификатора пиктограммы приложения. Этот элемент создан MFC AppWizard и не представляет для нас особого интереса. Остальные элементы данных класса добавлены нами через окно Project Workspace и мы рассмотрим их более подробно.

Полный путь каталога, содержимое которого отображается в списке на диалоговой панели, хранится в строке sCurentDirectory. Этот путь устанавливается, когда приложение соединяется с сервером FTP, а потом изменяется, когда вы входите в каталог, отображаемый в списке или выходите в каталог верхнего уровня, нажимая кнопку On top.

В списке IDC_FTP_LIST отображаются названия каталогов и файлов. Для наглядности мы выделяем их различными пиктограммами. Список изображений формируется во время инициализации диалоговой панели методом OnInitDialog и указатель на него записывается в элемент данных m_ImageList класса CFtpViewDlg.

Два последних элемента данных, входящих в класс CFtpViewDlg, наиболее интересны, так как используются для доступа к сети Internet и серверу FTP.

Элемент m_InternetSession является указателем на объект класса CInternetSession. Этот объект создается во время инициализации диалоговой панели с помощью метода OnInitDialog и требуется для дальнейшей работы с классами WinInet.

Элемент m_FtpConnection представляет собой указатель на объект класса CFtpConnection. Этот объект создается при установлении соединения с сервером FTP, которое выполняется методом OnConnect, когда пользователь нажимает кнопку Connect.

Таблица сообщений класса 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. Сообщение с таким кодом извещения вырабатывается списком, когда пользователь делает в нем двойной щелчок левой клавишей мыши.

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

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


CFtpViewDlg dlg;

Конструктор класса CFtpViewDlg вызывает конструктор своего базового класса CDialog. При этом ему передается идентификатор диалоговой панели IDD и идентификатор главного окна приложения pParent. Диалоговая панель нашего приложения имеет идентификатор IDD_FTPVIEW_DIALOG, но в определении класса CFtpViewDlg указано, что IDD соответствует IDD_FTPVIEW_DIALOG:


enum { IDD = IDD_FTPVIEW_DIALOG };

В теле конструктора расположен блок AFX_DATA_INIT. В нем ClassWizard поместил код инициализации элемента данных класса m_FtpAddress. Конструктор также инициализирует m_hIcon, записывая в него идентификатор пиктограммы IDR_MAINFRAME:


//============================================================
// Конструктор класса CFtpViewDlg 
//============================================================
CFtpViewDlg::CFtpViewDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CFtpViewDlg::IDD, pParent)
{
   //{{AFX_DATA_INIT(CFtpViewDlg)
   m_FtpAddress = _T("");
   //}}AFX_DATA_INIT

   // При использовании метода LoadIcon нет необходимости 
   // вызывать DestroyIcon 
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
Метод DoDataExchange

С помощью MFC ClassWizard мы привязали к органам управления диалоговой панели несколько переменных. Всю работу по связыванию этих переменных и органов управления выполняет метод DoDataExchange XE "CWnd:DoDataExchange" . В блоке AFX_DATA_MAP размещены вызовы соответствующих методов DDX XE "DDE" XE "DDV" :


//============================================================
// Метод DoDataExchange класса CFtpViewDlg
//============================================================
void CFtpViewDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CFtpViewDlg)
   DDX_Control(pDX, IDC_STATUS, m_Status);
   DDX_Control(pDX, IDOK, m_Ok);
   DDX_Control(pDX, IDC_ON_TOP, m_OnTop);
   DDX_Control(pDX, IDC_CONNECT, m_Connect);
   DDX_Control(pDX, IDC_FTP_LIST, m_FtpList);
   DDX_Text(pDX, IDC_FTP_ADDRESS, m_FtpAddress);
   //}}AFX_DATA_MAP
}

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

Только последний метод в блоке AFX_DATA_MAP отличается от остальных. Это метод DDX_Text, который используется для обмена данными между полем редактирования IDC_FTP_ADDRESS и строкой m_FtpAddress. Обмен выполняется при обращении к методу UpdateData.

Метод DoDataExchange класса CFtpViewDlg создается и модифицируется средствами MFC AppWizard и MFC ClassWizard. В большинстве случаев от вас не потребуется изменять этот метод вручную.

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

Методы OnPaint и OnQueryDragIcon

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

Метод OnConnect

Метод OnConnect класса CFtpViewDlg вызывается, когда вы нажимаете кнопку Connect чтобы соединится с сервером FTP. Адрес сервера FTP, с которым устанавливается соединение, должен быть введен в поле редактирования IDC_FTP_ADDRESS. Это поле располагается на диалоговой панели с левой стороны от кнопки Connect.

Перед тем как устанавливать соединение с сервером FTP, метод OnConnect блокирует кнопки управления, расположенные на диалоговой панели - Connect, OK и On top. Блокировка выполняется с помощью метода EnableWindow, определенного в классе CWnd. Этот метод вызывается для объектов m_Connect, m_Ok и m_OnTop класса CButton, представляющих эти кнопки:


// Блокируем кнопки Connect, OK и On Top
m_Connect.EnableWindow(FALSE);
m_Ok.EnableWindow(FALSE);
m_OnTop.EnableWindow(FALSE);

Если вы ранее уже соединились с сервером FTP и указатель m_FtpConnection не равен значению NULL, разрываем эту связь и удаляем объект m_FtpConnection. Затем присваиваем m_FtpConnection значение NULL:

if (m_FtpConnection != NULL) { m_FtpConnection -> Close(); delete m_FtpConnection; m_FtpConnection = NULL; }

Пользователь мог изменить адрес сервера, с которым надо установить соединение, поэтому обновляем строку m_FtpAddress, считывая значение из поля редактирования IDC_FTP_ADDRESS. Для этого опять (раньше в методе OnInitDialog) вызываем метод UpdateData, но теперь указываем ему параметр TRUE. Он означает, что состояние органов управления диалоговой панели должно быть записано в привязанные к ним переменные:


UpdateData(TRUE);

Теперь, когда адрес сервера известен, пытаемся с ним соединиться. Для этого вызываем метод GetFtpConnection класса CInternetSession объекта. В случае ошибки метод GetFtpConnection может вызвать исключение CInternetException.

Чтобы организовать обработку этого исключения помещаем вызов метода GetFtpConnection в блок try и создаем соответствующий блок catch:


// Пытаемся соединиться с сервером FTP 
try
{
   // Меняем форму курсора (курсор "ожидание")
   CWaitCursor wait;   

   // Соединяемся с сервером FTP. Эта операция
   // может вызвать исключение CInternetException
   m_FtpConnection = 
      m_InternetSession->GetFtpConnection(m_FtpAddress);
}

Так как процесс соединения с сервером может занять достаточно много времени, изменяем форму курсора. Для этого достаточно создать объект wait класса CWaitCursor. Курсор примет свою прежнюю форму автоматически, когда объект wait будет удален. В данном случае это произойдет при выходе из блока try.

В случае если работа метода GetFtpConnection вызовет исключение CInternetException, вызывается соответствующий блок catch. Такое может произойти, например, если сервер FTP с заданным адресом не существует или он не работает в данное время.

Обработчик исключения вызывает для него метод GetErrorMessage. Он возвращает текстовое описание причины исключения, которое затем отображается на экране с помощью функции AfxMessageBox. Если метод GetErrorMessage не может вернуть текстового описания, то на экране отображается сообщение GetFtpConnection Error.

Чтобы завершить обработку исключения удаляем его, вызывая для исключения метод Delete. Так как исключение означает, что установить соединение с FTP сервером не удалось, присваиваем указателю m_FtpConnection значение NULL:


catch (CInternetException* pEx)
{
   // Обрабатываем исключение CInternetException
   TCHAR szErr[1024];  // временный буфер для сообщения

   // Выводим сообщение об ошибке
   if (pEx->GetErrorMessage(szErr, 1024))
      AfxMessageBox(szErr);
   else
      AfxMessageBox("GetFtpConnection Error");

   // Удаляем иссключение
   pEx->Delete();

   // Обнуляем указатель m_FtpConnection
   m_FtpConnection = NULL;
}

Если соединение не установлено, тогда выводим соответствующую надпись в поле IDC_STATUS, снимаем блокировку кнопок Connect, OK и On Top и завершаем работу метода. Чтобы снять блокировку кнопок, вызываем для управляющих ими объектов метод EnableWindow с параметром TRUE:


if( m_FtpConnection == NULL )
      m_Status.SetWindowText("Connect not established");
...
m_Connect.EnableWindow(TRUE);
m_Ok.EnableWindow(TRUE);
m_OnTop.EnableWindow(TRUE);

Если соединение с сервером FTP установлено успешно, определяем текущий каталог. Для этого вызываем метод GetCurrentDirectory, передавая ему в качестве параметра строку sCurrentFtpDirectory. Эта строка определена в методе OnConnect как объект класса CString:


// Определяем текущий каталог сервера FTP 
BOOL fResult= 
   m_FtpConnection -> 
      GetCurrentDirectory(sCurrentFtpDirectory);
if(fResult)
   sCurentDirectory = sCurrentFtpDirectory;
else 
   AfxMessageBox("GetCurrentDirectory Error");

В случае успешного завершения метод GetCurrentDirectory запишет в строку sCurrentFtpDirectory полный путь текущего каталога и мы скопируем его в строку sCurentDirectory, являющуюся элементом класса CFtpViewDlg.

Узнав текущий каталог, вызываем метод DirectoryView, определенный в классе CFtpViewDlg, который считывает имена файлов и каталогов сервера FTP и отображает их в списке на диалоговой панели. Перед вызовом метода DirectoryView мы создаем объект класса CWaitCursor, поэтому во время длительного процесса опроса текущего каталога сервера курсор изменит свою форму. Заметим, что после заполнения списка при выходе управления из блока в котором определен объект wait класса CWaitCursor, форма курсора автоматически восстанавливается:


// Меняем форму курсора (курсор "ожидание")
CWaitCursor wait;   

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

После того, как содержимое каталога выведено в списке на диалоговой панели, отображаем в поле IDC_STATUS путь каталога и снимаем блокировку с кнопок Connect, OK и On Top:


// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);
Метод DirectoryView

Метод DirectoryView класса CFtpViewDlg выполняет львиную долю работы всего нашего приложения. Именно этот метод после соединения с сервером FTP определяет названия и основные характеристики объектов в заданном каталоге сервера, а затем отображает их в списке IDC_FTP_LIST на главной диалоговой панели приложения:


BOOL CFtpViewDlg::DirectoryView()
{
   // Переменная, сигнализирующая о получении последнего 
   // элемента каталога
   BOOL     fResult; 

   // Временная переменная, определяющая тип объекта -
   // файл или каталог 
   BOOL     fDirectory;

Метод DirectoryView заполняет список IDC_FTP_LIST именами каталогов и файлов сервера FTP. Чтобы удалить из списка элементы, занесенные в него ранее, вызываем метод DeleteAllItems для объекта m_FtpList, управляющего этим списком (см. метод DoDataExchange):


// Удалить все элементы из списка IDC_FTP_LIST
m_FtpList.DeleteAllItems();

Для поиска файлов и каталогов на сервере FTP предназначен класс CFtpFileFind, входящий в состав библиотеки классов MFC. Метод DirectoryView создает объект этого класса. В качестве параметра конструктору класса CFtpFileFind передается указатель m_FtpConnection на объект, представляющий соединение с сервером FTP:


CFtpFileFind m_FtpFileFind(m_FtpConnection);

Далее мы вызываем для только что созданного объекта m_FtpFileFind метод FindFile. Он выполняет поиск каталогов и файлов в каталоге, путь которого указан в качестве параметра:


// Получаем имена всех объектов текущего каталога
if(fResult = 
   m_FtpFileFind.FindFile(_T(sCurentDirectory + "/*")))
{
        . . .
}

Если каталог не содержит других объектов - каталогов и файлов, или в случае возникновения ошибки, метод FindFile возвращает ненулевое значение. Тогда на экран выводится сообщение File's not found or error и работа метода DirectoryView завершается.

В случае, если метод FindFile завершается успешно, это означает, что указанный каталог содержит другие каталоги или файлы. Тогда мы начинаем в цикле вызывать метод FindNextFile. Он получает имя очередного файла или каталога и мы отображаем его вместе с некоторой дополнительной информацией в списке объектов каталога. Когда метод FindNextFile вернет значение FALSE, это значит, что мы считали последний объект каталога. В этом случае мы добавляем этот последний объект к списку, завершаем цикл и выходим из метода DirectoryView:


for(int n = 0;fResult; n++)
{
   // Получаем очередной объект из каталога
   fResult = m_FtpFileFind.FindNextFile();
   . . .
}

После обращения к методу FindNextFile можно вызывать другие методы класса CFtpFileFind. Они позволяют определить различные характеристики обнаруженных объектов. Например, вы можете узнать являются ли они каталогами или файлами, получить их имя, размер, дату создания и т. д.

Так, сначала мы определяем, является ли очередной объект, полученный методом FindNextFile файлом или каталогом и записываем полученный результат в fDirectory. Если мы получили каталог, то метод IsDirectory возвращает значение 1, а если файл, то - 0. Мы будем использовать значение fDirectory, сравнивая его с константой DIRECTORY, определенной как 0 и FILE, определенной как 1 в файле FtpViewDlg.h.

Далее с помощью метода GetFileName определяется имя объекта (файла или каталога) и записывается во временную строку fileName класса CString:


CString   fileName;

// Определяем имя объекта
fileName = m_FtpFileFind.GetFileName();

Теперь, когда мы узнали тип объекта и его имя можно добавить элемент к первой колонке в списке. Для этого необходимо заполнить поля структуры lv_item и передать ее методу InsertItem объекта, управляющего списком:


// Структура для добавления нового элемента к списку
LV_ITEM   lv_item;   
. . .
// Заполняем структуру lv_item, сведениями об 
// очередном объекте каталога сервера FTP. Указываем, 
// что в список добавляется текст и изображение
lv_item.mask = LVIF_TEXT | LVIF_IMAGE; 
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем первую колонку
lv_item.iSubItem = 0;
// Выбираем изображение для нового элемента списка в 
// зависимости от типа объекта 
lv_item.iImage = (fDirectory) ? DIRECTORY : FILE;
// Указываем имя каталога или файла
lv_item.pszText = fileName.GetBuffer(MIN_LEN_BUF);

 // Добавляем новый элемент к списку IDC_FTP_LIST
 m_FtpList.InsertItem(&lv_item);

После заполнения первой колонки списка необходимо заполнить вторую, записав в нее длину файла или, если данный элемент списка описывает каталог, то строку Dir. Точно также как в случае с именем объекта, сначала заполняется структура lv_item, а затем она передается методу InsertItem.

Обратите внимание, что в отличие от первой колонки, во второй колонки не используется пиктограмма, поэтому в поле mask структуры lv_item заносится только константа LVIF_TEXT. Другое отличие состоит в том, что полю iSubItem структуры lv_item присваивается значение 1, указывающее, что мы будем добавлять элемент во вторую колонку (нумерация iSubItem начинается с нуля):


//============= Определяем длину файла =============
// Длинна файла
DWORD dwLength = 0;
// Временные строка для формирования текстового 
// представления длинны файла
CString sLength;

// Заполняем колонку Length для новой записи и 
// записываем в нее длину файла или строку Dir, если 
// новый объект - каталог.
// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT; 
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем вторую колонку
lv_item.iSubItem = 1;
// Если очередной объект является каталогом, то 
// вместо в колонке Length записываем строку Dir
if(fDirectory)
{
   lv_item.pszText = "Dir";
}

// Если очередной объект является файлом, то 
// записываем его длину в колонку Length 
else
{
   // Определяем длину файла
   dwLength = m_FtpFileFind.GetLength();
   // Формируем текстовое представление длины файла
   sLength.Format("%d", dwLength);
   lv_item.pszText = sLength.GetBuffer(MIN_LEN_BUF);
}
        
// Добавляем запись во второй колонке списка (колонка 
// Length)
m_FtpList.SetItem(&lv_item);

Теперь остается заполнить только третью и четвертую колонки списка, в которых надо записать дату и время создания или изменения файла (каталога). Для определения этих данных вызывается метод GetLastWriteTime класса CFtpFileFind, который записывает полученную информацию в объект mTime класса CTime:


// Дата и время создания каталога или файла
CTime mTime;  
// Временные строки для формирования текстового 
// представления даты и времени 
CString sDate;
CString sTime;

// Определяем время изменения файла или каталога
if(!m_FtpFileFind.GetLastWriteTime(mTime))
   break;

Затем мы подготавливаем структуру lv_item, заполняя ее поля соответствующими значениями:


// Добавляется только текст без пиктограммы
lv_item.mask = LVIF_TEXT; 
// Указываем номер строки в списке
lv_item.iItem = n;
// Заполняем третью колонку
lv_item.iSubItem = 2;
// Выделяем из объекта mTime день, месяц и год 
sDate = mTime.Format("%d.%m.%y");
// Записываем сформированную дату в структуру lv_item
lv_item.pszText = sDate.GetBuffer(MIN_LEN_BUF);

Чтобы добавить запись в колонке Date обращаемся к методу SetItem и передаем ему указатель на структуру lv_item:


// Добавляем запись во второй колонке списка (колонка Date)
m_FtpList.SetItem(&lv_item);

Затем мы немного модифицируем только что заполненную структуру lv_item, изменяя в ней только поля iSubItem и pszText:


// Заполняем четвертую колонку, записываем в нее 
// время последнего изменения файла (каталога)
lv_item.iSubItem = 3;
// Выделяем из объекта mTime часы, минуты и секунды 
sTime = mTime.Format("%H:%M:%S");
// Записываем сформированную строку, содержащую время
lv_item.pszText = sTime.GetBuffer(MIN_LEN_BUF);

Опять вызываем метод SetItem, передавая ему модифицированную структуру lv_item. Этот вызов добавит информацию о времени последнего изменения файла в колонке Time:


// Добавляем запись во второй колонке списка (колонка Time)
m_FtpList.SetItem(&lv_item);

Когда при очередном вызове метод FindNextFile возвращает нулевое значение, значит найден последний объект из каталога. В список добавляется последняя строка и на этом работа цикла заполнения списка завершается. Заканчиваем поиск объектов в данном каталоге вызывая метод Close и возвращаем управление с помощью оператора return:


// Заканчиваем поиск объектов в каталоге, закрываем 
// объект m_FtpFileFind
m_FtpFileFind.Close();
Метод OnDblclkFtpList

В списке IDC_FTP_LIST показываются имена файлов и каталогов сервера FTP. Файлы выделяются пиктограммой , а каталоги пиктограммой и строкой Dir в столбце Length. Чтобы вы имели возможность просмотра не только того каталога, с которым вас по умолчанию соединил сервер FTP, но также и других вложенных каталогов, мы предусмотрели метод OnDblclkFtpList.

¨     Наше приложение не меняет текущий каталог сервера FTP. Для этого предназначен метод SetCurrentDirectory класса CFtpConnection. Изменяется только каталог, в котором осуществляется поиск файлов и других каталогов

Метод OnDblclkFtpList вызывается когда пользователь выполняет двойной щелчок левой клавишей мыши по имени каталога или имени файла. В качестве параметров методу OnDblclkFtpList передаются указатель pNMHDR на структуру типа NMHDR и указатель pResult на переменную типа LRESULT.

Рассмотрим метод OnDblclkFtpList более подробно. Сначала он блокирует список IDC_FTP_LIST, вызывая метод EnableWindow с параметром FALSE:


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

// Блокируем список IDC_FTP_LIST
m_FtpList.EnableWindow(FALSE);

Затем с помощью метода GetItemCount мы определяем количество элементов в списке IDC_FTP_LIST и записываем его во временную переменную iTotalNumber типа int:


iTotalNumber = m_FtpList.GetItemCount();

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

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


// Определяем, какой объект списка выбран
for(int i = 0; i < iTotalNumber; i++)
{
  if(LVIS_SELECTED == m_FtpList.GetItemState(i,LVIS_SELECTED))
  {
     . . .
     break;
  }
}

Если метод m_FtpList.GetItemState( i, LVIS_SELECTED )) возвращает значение LVIS_SELECTED, значит элемент с индексом i выбран из списка. В этом случае определяем имя соответствующего объекта и его размер, считывая их непосредственно из первой и второй колонки списка:


// Определяем название выбранного элемента списка
// (имя файла или каталога)
sSelItem = m_FtpList.GetItemText( i, 0 );

// Считываем данные из колонки Length
sLength_Dir = m_FtpList.GetItemText( i, 1 );

Если вместо длины файла во второй колонке записана строка Dir, значит из списка выбран каталог. Тогда мы добавляем название этого каталога к концу строки, содержащей текущий каталог сервера FTP.

В противном случае вы выбрали из списка не каталог, а файл и на экран выводится предупреждающее сообщение, о том что вы выбрали файл с определенным именем:


if(sLength_Dir == "Dir") // Выбран каталог
   sCurentDirectory  = sCurentDirectory + "/" + sSelItem;
else                     // Выбран файл
   AfxMessageBox("You select file " + sSelItem);

После этого изменяем форму курсора и вызываем метод DirectoryView, который считывает имена объектов, расположенных в каталоге sCurentDirectory и отображает их в списке диалоговой панели приложения.

Чтобы изменить форму курсора мы создаем объект wait класса CWaitCursor. Когда при выходе из метода OnDblclkFtpList этот объект удаляется, курсор автоматически восстанавливает свою форму:


   . . .
   // Меняем форму курсора (курсор "ожидание")
   CWaitCursor wait;   

   // Отображаем содержимое выбранного каталога 
   DirectoryView();
}

Затем в поле IDC_STATUS отображается новый путь каталога и снимается блокировка со списка, так что мы опять можем выбирать из него файлы и каталоги:


// Отображаем на диалоговой панели новый путь каталога
m_Status.SetWindowText(sCurentDirectory);

// Снимаем блокировку списка IDC_FTP_LIST
m_FtpList.EnableWindow(TRUE);

В завершении метода OnDblclkFtpList мы записываем по адресу pResult нулевое значение, которое означает, что метод успешно завершен:


*pResult = 0;
Метод 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);
Деструктор класса CFtpViewDlg

Когда пользователь нажимает кнопку OK и закрывает диалоговую панель, вызывается деструктор класса CFtpViewDlg. Он последовательно закрывает соединение с сервером FTP и завершает сеанс связи с Internet.

Деструктор проверяет установлено ли соединение с сервером FTP - при этом указатель m_FtpConnection должен быть не равен значению NULL. Если это так, сначала вызывается метод Close, закрывающий соединение, а затем удаляется сам объект m_FtpConnection:


if (m_FtpConnection != NULL)
{
   m_FtpConnection -> Close();
   delete m_FtpConnection;
}

После того, как соединение с сервером FTP закрыто, завершаем сеанс связи с Internet. Для этого вызываем метод Close, а затем удаляем объект m_InternetSession:


if (m_InternetSession != NULL)
{
   m_InternetSession -> Close();
   delete m_InternetSession;
}

В завершение деструктор класса CFtpViewDlg удаляет список изображений m_ImageList:


delete m_ImageList;
[Назад] [Содеожание] [Дальше]