Г.Тяпичев
«Начальный курс быстрого программирования на СИ++». Глава 7.
Протоколы Интернета (вместо предисловия)
Сетевые компоненты в составе C++ Builder
Клиентское приложение для сервера ChServer
Приложение для работы с электронной почтой
Формирование и отсылка сообщений по электронной почте
Клиентское приложение по протоколу FTP
Интернет все сильнее и
быстрее вторгается в нашу жизнь. Многие из людей уже не могут себе представить
жизнь без «Всемирной Паутины» - Интернета. Поэтому очень большой число
программистов стало создавать свои программы для Интернета.
В последние годы на рынке
программных продуктов ведущие позиции заняли приложения класса клиент/сервер,
работающие в сети Интернета. Чтобы все эти разработки стали широко доступными,
потребовалось создать определенные правила, которые должны строго соблюдаться
каждым из разработчиков. Создание общедоступных приложений стало возможным
только благодаря использованию обязательных для всех разработчиков специальных
правил – протоколов. Прошли те времена, когда считалось высшим шиком
использовать в приложении протокол собственной «конструкции».
В этой главе вы сможете познакомиться с компонентами C++ Builder,
позволяющими разработчику быстро создать приложение, в котором используется
какой-либо из существующих стандартных протоколов. Сначала мы познакомим вас с
компонентами, которые поддерживают работу с сетями, а потом рассмотрим
приложение, в котором используются протоколы
POP, SMTP, FTP.
В C++ Builder
версий 3 и 4 все программные компоненты,
предназначенные для работы с сетью Internet, были сосредоточены в одной страницы библиотеки
компонентов – Internet. В
шестой версии C++ Builder разработчик компонентов – фирма Inprise –
разделила такого рода компоненты на целых пять групп. Первая группа
представлена, как и прежде, в странице библиотеки Internet. В нее входят компоненты TClientSocket и TServerSocket, а также разнообразные компоненты вида TPageProducer,
которые используются для создания приложений Web-серверов. Далеко не полный перечень компонентов,
находящихся в странице библиотеки Internet представлен в
табл. 7.1.
Тоблица
7.1. Компоненты в библиотеке Internet
Компонент |
Назначение |
TClientSocket |
Инкапсулирует Winsock для использования в клиентском приложении |
TServerSocket |
Инкапсулирует Winsock для использования в приложении-сервере |
TWebDispatcher |
Позволяет использовать модуль
данных в качестве расширения Web-сервера |
TPageProducer |
Используется для преобразования HTML-шаблона в HTML-документ |
TQueryTableProducer |
Используется для преобразования
объекта TQuery в HTML-таблицу |
TDataSetTableProducer |
Используется для преобразования
объекта TDataSet в HTML-таблицу |
TDataSetPageProducer |
Используется для включения в HTML-документ результатов, полученных объектом TDataSet |
TCppWebBrowser |
Инкапсулирует Web-броузер Internet Explorer для
использования его в клиентских приложениях |
Вторая группа компонентов
выведена в новую страницу библиотеки – FastNet. В нее включены ActiveX-компоненты FastNet, которые
можно использовать при разработке самых разнообразных сетевых приложений.
Полный список компонентов этой группы вы найдете в
табл. 7.2.
Таблица
7.2. Компоненты в библиотеке FastNet
Компонент |
Назначение |
TNMDayTime |
Используется для получения даты и
времени от Internet-сервера службы времени |
TNMMsg |
Используется для передачи простого
текстового сообщения в кодировке ASCII
по сети Internet или Intranet с помощью протокола TCP/IP |
TNMMsgServ |
Используется для создания сервера
обработки сообщений, переданных объектом класса TNMMsg |
TNMEcho |
Используются работы с эхо-сервером
Internet в процессе отладки приложений |
TNMFTP |
Используется для создания
клиентского приложения FTP |
TNMHTTP |
Устанавливает соединение с
сервером HTTP в сети World Wide Web |
TNMNNTP |
Используется для работы с сервером
новостей Internet и Intranet |
TNMStrm |
Отсылает потоки на сервер потоков
через Internet или Intranet |
TNMStrmServ |
Создает сервер потока, который
может получать потоки по сети Internet или Intranet |
TNMPOP3 |
Используется при создании
клиентского приложения для чтения сообщений, переданных по электронной почте
с использованием протокола POP3 |
TNMSMTP |
Используется при создании
клиентского приложения для передачи сообщений по электронной почте с
использованием протокола SMTP |
TNMTime |
Получает время от сервера времени Internet |
TNUDP |
Используется при реализации
пользовательского протокола передачи датаграмм (User Datagram Protocol – UDP) для пересылки датаграмм по сети Internet или Intranet |
TNMURL |
Выполняет преобразование URL в строку и строки в URL |
TNMUUProcessor |
Выполняет
кодирование/декодирование MIME-документов
или документов в нейтральном формате (uuencoded) |
Tpowersock |
Базовый класс для многих
сокет-компонентов на вкладке FastNet |
TNMGeneralServer |
Базовый класс для разработки
многопотоковых Internet-серверов |
TNMFinger |
Получает информацию о пользователе
из Internet с помощью протокола Finger |
Кроме приведенных выше
названий, компоненты для Интернета имеются:
q
в странице библиотеки WebSnap – 17
компонентов;
q
в странице
библиотеки WebServices – 7 компонентов;
q
в странице
библиотеки InternetExpress – 2 компонента.
Интересующиеся программированием для Интернет смогут найти
информацию по всем компонентам в полном руководстве для пользователей C++Builder 6.
Далее предлагаю вам в качестве учебных
пособий создать несколько проектов Windows
приложений для работы в сети Интернет. Фактически эти приложения являются как
бы игрушками, которые помогут вам понять и освоить важные и сложные процессы,
происходящие при различных видах связи в Интернете.
Первым примером мы рассмотрим программу поддержки общения
(«болтовни») через сеть Internet. Эта программа позволяет сразу нескольким
пользователям подключаться к центральному серверу и
каждый из них может передавать сообщения
другим пользователям, принимавшим в это время участие в обсуждении. Имя файла
проекта – ChServer.
Создаем директорию для
нового проекта. Обычным способом
(командой FileèNewèApplication)
создаем новый проект и сохраняем этот
новый проект в созданной для него директории. Файл с кодами назовем ChServ.cpp, а файл проекта будет называться ChServer.bpr.
Форма приложения с установленными на ней
компонентами изображена на рис. 7.1. Вы должны по этому рисунку установить
на форму, расположенную на экране вашего компьютера точно такие же компоненты
примерно в таком же расположении. Обратите внимание на то, что форма должна
иметь малые размеры (для удобства).
Рис. 7.1. Форма нового проекта
На форме установлены
следующие компоненты:
q компонент Panel занимает верхнюю часть формы, на этой
панели после будут установлены другие компоненты, свойство Caption = blank;
q компонент
Memo
располагается в нижней части формы, свойство align
этого компонента должно быть установлено как alClient, а
имя – LogMemo;
q установите
на панель две кнопки Button, верхней кнопке дать имя
StartBtn,
нижней – StopBtn, надписи на кнопках должны быть – Старт и Стоп;
q установить
на панели слева, внизу компонент SpeedButton и сделать надпись на кнопке Соединены сейчас;
q
установите
компонент Label и компонент Edit, назовите его PortEdit;
q добавьте
в форму компонент ServerSocket (его вы
найдете в библиотеке Internet) и присвойте ему
наименование MyServer.
Все
установленные на форме компоненты описаны в файле ChServ.h, размещенном в листинге
7.1.
Листинг
7.1. Файл ChServ.h
//----------------------------------------------------------------------
#ifndef ChServH
#define ChServH
//----------------------------------------------------------------------
#include
<Classes.hpp>
#include
<Controls.hpp>
#include <StdCtrls.hpp>
#include
<Forms.hpp>
#include
<ExtCtrls.hpp>
#include
<ScktComp.hpp>
#include
<Buttons.hpp>
//----------------------------------------------------------------------
class TForm1 : public
TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
TMemo *LogMemo;
TEdit *PortEdit;
TLabel *PortLabel;
TServerSocket *MyServer;
TButton *StartBtn;
TButton *StopBtn;
TSpeedButton *SpeedButton1;
void __fastcall StartBtnClick(TObject
*Sender);
void __fastcall StopBtnClick(TObject
*Sender);
void __fastcall FormCreate(TObject
*Sender);
void __fastcall
MyServerClientConnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall
MyServerClientDisconnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall SpeedButton1Click(TObject
*Sender);
void __fastcall MyServerClientRead(TObject
*Sender,
TCustomWinSocket *Socket);
private: // User declarations
TStringList *ConnectedList;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
void __fastcall SendMessage(AnsiString
aMessage, AnsiString aFrom);
};
//----------------------------------------------------------------------
extern PACKAGE TForm1
*Form1;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
При создании такого файла в
своем проекте не забудьте, что разделы private: и public: следует
переписать из листинга 7.1 в аналогичный файл своего проекта. Следует помнить,
что только раздел __published: создается
программой автоматически по мере установки компонентов на форму.
После окончания установки
компонентов на форму, переходим к работе с текстовым файлом ChServ,cpp, текст
которого размещен в листинге 7.2.
Еще раз напоминаю, что вы
не должны просто скопировать текст из листинга 7.2 в файл своего проекта,
который называется ChServ.cpp. Проект в таком случае не будет работать, т.к. не
будет связи между компонентами, установленными на форме проекта и функциями в
тексте файла.
Еще раз напоминаю
правило: для того, чтобы записать в файл *.cpp текст какой то функции необходимо
выполнить двойной щелчок кнопкой мышки на изображении нужного компонента или на
одном из его событий. Только после этого действия на экране появится окно
текстового редактора, в котором курсор будет находиться в теле именно той функции, которую следует заполнить текстом.
Рассмотрим текст файла ChServ.cpp в листинге
7.2 и по его подобию заполним текстами функции файла с аналогичным именем в
своем создаваемом проекте.
Функция 1 описывает
состояние формы и мы не будем добавлять в неё что
либо.
Функция 2 является реакцией
на нажатие кнопки Старт. Двойной
щелчок на изображении этой кнопки вызывает окно редактора с подготовленной
заготовкой для аналогичной функции в файле проекта. Переписываем текст из
функции 2 (листинг 7.2) в аналогичную функцию файла проекта.
Функция 3 является реакцией на нажатие клавиши Стоп.
Поступаем точно также, как и в предыдущем
случае. Переписываем текст функции 3,
расположенной в листинге, в тело заготовки под аналогичную функцию в файле
проекта.
Функция 4 создается в ответ на событие OnCreate главной формы. Чтобы вызвать окно редактора с
заготовкой под эту функцию нужно:
q
активизировать
главную форму – Form;
q
зайти на страницу Events (события) и
выбрать OnCreateèFormCreate.
После этого двойной щелчок на событии FormCreate выдает на экран окно редактора текста с заготовкой
под функцию 4. Тело этой заготовки следует заполнить по образцу из листинга
7.2.
Функции 5, 6 и 8 являются ответами на
события компонента MyServer. Все три
аналогичные функции в файле проекта заполняются одинаково:
q
активизируется
компонент MyServer;
q
в окне Object Inspector заходим на страничку Events (события) и
выбираем OnClientConnectèMyServerClientConnect. Двойной щелчок на выбранном событии и перед нами
окно редактора с заготовкой под соответствующую функцию, аналогичную функции 5;
q
Для функции 6
выбираем событие OnClientDisconnectèMyServerDisconnect и заполняем аналог функции 6;
q
Для функции 8
выбираем событие OnClientReadèMyServerClientRead и заполняем аналог функции 8.
Листинг 7.2. Файл ChServ.cpp
//-----------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "ChServ.h"
//----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent*
Owner) // 1
: TForm(Owner)
{
}
//----------------------------------------------------------------------
//
void __fastcall
TForm1::StartBtnClick(TObject *Sender)
// 2
{
MyServer->Port = PortEdit->Text.ToIntDef(1971);
Caption = MyServer->Port;
MyServer->Active = true;
StopBtn->Enabled = true;
StartBtn->Enabled = false;
PortEdit->Enabled = false;
}
//----------------------------------------------------------------------
//
void __fastcall
TForm1::StopBtnClick(TObject *Sender)
// 3
{
MyServer->Active = false;
StartBtn->Enabled = true;
StopBtn->Enabled = false;
PortEdit->Enabled = true;
}
//----------------------------------------------------------------------
//
void __fastcall
TForm1::FormCreate(TObject *Sender)
// 4
{
ConnectedList = new TStringList();
}
//----------------------------------------------------------------------
void __fastcall
TForm1::MyServerClientConnect(TObject *Sender,
TCustomWinSocket *Socket) // 5
{
LogMemo->Lines->Add(Socket->RemoteAddress + "
Соединен.");
ConnectedList->AddObject(Socket->RemoteAddress, Socket);
}
//----------------------------------------------------------------------
void __fastcall
TForm1::MyServerClientDisconnect(TObject *Sender,
TCustomWinSocket *Socket) // 6
{
LogMemo->Lines->Add(Socket->RemoteAddress + "
Разъединен.");
SendMessage(Format("%s
разъединен.",OPENARRAY(TVarRec,(ConnectedList->
Strings[ConnectedList->IndexOfObject(Socket)]))),"Server");
ConnectedList->Delete(ConnectedList->IndexOfObject(Socket));
}
//---------------------------------------------------------------------
void __fastcall
TForm1::SpeedButton1Click(TObject *Sender) // 7
{
LogMemo->Lines->Assign(ConnectedList);
}
//----------------------------------------------------------------------
void __fastcall
TForm1::MyServerClientRead(TObject *Sender,
TCustomWinSocket *Socket) // 8
{
AnsiString TextIn, CurrentName;
int iIndex;
TextIn = Socket->ReceiveText();
//
LogMemo->Lines->Add(TextIn);
iIndex = ConnectedList->IndexOfObject(Socket);
if(iIndex == -1)
return;
TStringList *UserName = new TStringList();
if(TextIn.Pos("UserName=") == 1)
{
//Set User Name
UserName->Text = TextIn;
ConnectedList->Strings[iIndex] =
UserName->Values["UserName"];
SendMessage(Format("%s соединен.",
OPENARRAY(TVarRec,(UserName->Values["UserName"]))),"Server");
}
else
{
//Send Message
CurrentName = ConnectedList->Strings[iIndex];
SendMessage(TextIn, CurrentName);
}
delete UserName;
}
//----------------------------------------------------------------------
void __fastcall
TForm1::SendMessage(AnsiString aMessage, AnsiString aFrom)
// 9
{
for(int i=0;i<MyServer->Socket->ActiveConnections;i++)
{
if(aFrom == "Server")
MyServer->Socket->Connections[i]->
SendText(Format("%s",OPENARRAY(TVarRec,(aMessage))));
else
MyServer->Socket->Connections[i]->SendText(Format("%s
сообщил: %s", OPENARRAY(TVarRec,(aFrom, aMessage))));
}
}
//----------------------------------------------------------------------
Функция 7 является реакцией на
нажатие кнопки Соединены сейчас. Аналог этой функции в файле
проекта заполняется точно так же, как это делалось для функций 2 и 3.
Функция 9 не связана с компонентом на форме и
объявлена в файле ChServ.h. Текст этой функции,
прямо с заголовком, должен быть переписан
из листинга 7.2 в файл ChServ.cpp, входящий в состав проекта.
Если все функции в файле проекта записаны без
ошибок, то сервер можно запустить в работу. Вид рабочего окна этой программы
изображен на рис. 7.2.
Рис. 7.2.
Окно работающего сервера
Теперь программа способна установить номер
порта, который будет использовать сервер, а также запустить сервер в работу и
остановить его. Номера портов позволяют единственной системе,
идентифицированной по значениям свойств Host или Address,
работать с множеством подключений одновременно. Примерами номеров портов по
умолчанию могут быть 80 – для
Web-серверов и 23 – для серверов Telnet. Для нашего сервера ChServer значением номера порта по умолчанию будет 1971. Номера портов могут иметь
значения в диапазоне от 1 до 9999.
Программа должна
соответствующим образом реагировать на регистрацию пользователей на сервере и
их отключение от сервера. Это выполняет следующий код обработки событий OnClientConnect:
Void _fastcal1
TForm1::MyServerClientConnect(TObject
*Server,
TCusttomWinSocket
*Socket) // 5
{
LogMemo->Lines->Add(Socket->RemoteAddress,
Socket);
}
Тем самым в журнал LogMemo
добавляется строка с адресом клиента, который собирается подключиться к
серверу. Затем в список ConnectedList добавляется сокет,
через который подключается клиент, причем в качестве идентификатора
используется адрес клиента.
Обработка события OnClientDisconnect
выполняется в функции 6.
Код этой функции добавляет строку в журнал LogMemo,
которая регистрирует отключение клиента от сервера. Затем вызывается метод SendMessage(), которая
информирует всех подключенных в текущий момент клиентов о том, что один из
«собственников» покинул форум.
Эта функция опрашивает в цикле все подключенные
объекты Socket и
отсылает через них сообщение вида User said:
text (<Пользователь> заявил:
<текст>). Сообщения, поступающие на сервер от клиентов, можно разделить
на две категории: реплики, предназначенные для рассылки остальным участникам
форума, и “приватное” сообщение, предназначенное только серверу, в котором
пользователь передает свое имя.
Если же окажется, что
сообщение начитается не с текста “UsreName=”, то полученный текст рассылается всем прочим
участникам обсуждения с помощью метода SendMessage().
Итак, мы завершили
разработку программы сервера, который поддерживает коллективное обсуждение
через сеть Internet. Сервер позволяет
множеству клиентов оперативно обмениваться текстовыми сообщениями.
В этом разделе мы
рассмотрим программу клиентского приложения, которое будет работать с описанным
выше сервером ChServer. Это
приложение будет использовать протокол TCP/IP. С помощью этого простого клиентского
приложения пользователь сможет указать свое имя, выбрать сервер и порт, к
которому собирается подключиться, отсылать и получать сообщения. При желании
эту программу можно усовершенствовать
– добавить в нее сопровождение списка пользователей, частных форумов и
опции форматирования текстов.
Начните работу, как
обычно, с создания директории для нового
приложения. Затем в среде C++ Builder создаем проект нового приложения. Для
этого в меню выбираем команду FileèNewèApplication – будет сформирована пустая экранная форма и заготовки
для файлов с программными кодами.
Теперь нужно сохранить
созданный проект в отведенной для него директории (командой FileèSave Project Ass) и при этом
назначить имя для файла, в котором содержатся исходные коды, - назовем его CClient.cpp, а файлу
проекта дадим имя Client.bpr.
Далее займемся компоновкой экранной формы.
Выполните следующие операции.
Поместите в экранную форму такие компоненты: два
компонента TMemo,
которым присвойте имена MemoIn (займет
всю центральную часть формы) и MemoOut (узкая полоска в нижней части формы), а
также компонент TPanel (в
верхней части формы),свойство
Caption которого очистите.
1. Значения
свойств align этих новых компонентов настройте
следующим образом: для компонента MemoOut установите
значение alBotton, для
компонента Panel1
– alTop, а для компонента MemoIn – alClient.
2. Компоненту
MemoIn присвойте статус «только для чтения»,
поскольку этот компонент будет только выводить сообщения, поступившие от
сервера. Настройка статуса выполняется присвоением свойству ReadOnly элемента MemoIn значения True.
3. Сразу
после запуска клиентское приложение еще не подключено к серверу программы.
Поэтому имеет смысл в этом режиме заблокировать компонент MemiOut чтобы пользователь не пытался отсылать
сообщения, прежде чем подключится к серверу. Присвойте свойству Enabled компонента
MemoOut значение
False.
4. Теперь,
покончив с базовыми компонентами пользовательского интерфейса, займемся
прочими.
·
Включите в состав Panel1
кнопку (компонент TButton) и присвойте ее
свойству name значение DisconnectButton,
а свойству Caption – значение Disconnect.
·
Добавьте в панель еще
одну кнопку и присвойте ее свойству name значение
DisConnectButton,
а свойству Caption – значение Disconnect.
·
Добавьте в Panel1
три компонента TEdit; присвойте
им имена UserNameEdit,
PortEdit и ServerEdit. Свойству
Text всех
трех новых компонентов присвойте значение blank.
5. Добавьте
в Panel1
три компонента TLabel и
установите в них надписи User Name, Port и Server.
Далее
добавим в экранную форму Form1 компонент TClientSocket.
Присвойте свойству name этого
компонента значение MySocket. Класс TClientSocket инкапсулирует все функции сокета,
необходимые для работы клиентского сетевого приложения. Изображение этого
компонента на поле экранной формы напоминает штепсельную розетку, достаточно
точно передавая его назначение.
Компоновка
элементов управления на поле экранной формы должна выглядеть примерно так, как
на рис. 7.3.
Рис. 7.3.
Форма клиентского приложения
Чтобы не допустить ошибки с
заполнением формы, в листинге 7.3 разместим текст
файла CClient.h.
Листинг
7.3. Файл
CClient.h
//----------------------------------------------------------------------
#ifndef CClientH
#define CClientH
//----------------------------------------------------------------------
#include
<Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<ExtCtrls.hpp>
#include
<ScktComp.hpp>
//----------------------------------------------------------------------
class TForm1 : public
TForm
{
__published: // IDE-managed Components
TMemo *MemoOut;
TPanel *Panel1;
TEdit *UserNameEdit;
TLabel *Label1;
TEdit *PortEdit;
TLabel *Label2;
TButton *ConnectBtn;
TButton *DisconnectBtn;
TMemo *MemoIn;
TEdit *ServerEdit;
TLabel *Label3;
TClientSocket *MySocket;
void __fastcall ConnectBtnClick(TObject
*Sender);
void __fastcall MySocketConnect(TObject
*Sender,
TCustomWinSocket *Socket);
void __fastcall DisconnectBtnClick(TObject
*Sender);
void __fastcall MemoOutKeyPress(TObject
*Sender, char &Key);
void __fastcall MySocketRead(TObject
*Sender,
TCustomWinSocket *Socket);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------
extern PACKAGE TForm1
*Form1;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
Не
буду подробно описывать этот файл – подобные файлы описывались неоднократно и
вы должны уже четко себе представлять структуру этого файла, а также в какие его разделы следует вносить изменения, а в
какие – этого делать нельзя.
Далее
рассмотрим файл CClient.cpp, в котором содержатся исходные коды создаваемого
приложения. Текст этого файла размещен в листинге 7.4.
Листинг 7.4.
Файл CClient. cpp
//----------------------------------------------------------------------
#include
<vcl.h>
#pragma
hdrstop
#include
"CClient.h"
//----------------------------------------------------------------------
#pragma
package(smart_init)
#pragma
resource "*.dfm"
TForm1
*Form1;
//----------------------------------------------------------------------
__fastcall
TForm1::TForm1(TComponent* Owner)
// 1
: TForm(Owner)
{
Caption = "ChClient";
}
//----------------------------------------------------------------------
void
__fastcall TForm1::ConnectBtnClick(TObject *Sender) // 2
{
MySocket->Address = ServerEdit->Text;
MySocket->Port =
PortEdit->Text.ToIntDef(1971);
MySocket->Active = true;
ConnectBtn->Enabled = false;
DisconnectBtn->Enabled = true;
MemoOut->Enabled = true;
}
//-----------------------------------------------------------------------
void
__fastcall TForm1::MySocketConnect(TObject *Sender,
TCustomWinSocket *Socket) // 3
{
MemoIn->SetFocus();
MemoIn->Lines->Add("Connected
...");
MemoOut->SetFocus();
MySocket->Socket->SendText(Format("UserName=%s",OPENARRAY(TVarRec,(UserNameEdit->Text))));
}
//-----------------------------------------------------------------------
void
__fastcall TForm1::DisconnectBtnClick(TObject *Sender) // 4
{
MySocket->Active = false;
ConnectBtn->Enabled = true;
DisconnectBtn->Enabled = false;
MemoOut->Enabled = false;
}
//----------------------------------------------------------------------
void
__fastcall TForm1::MemoOutKeyPress(TObject *Sender, char &Key) //5
{
if(Key == VK_RETURN)
{
MySocket->Socket->SendText(MemoOut->Text);
MemoOut->Lines->Clear();
Key = 0;
}
}
//----------------------------------------------------------------------
void
__fastcall TForm1::MySocketRead(TObject *Sender,
TCustomWinSocket *Socket) // 6
{
MemoIn->SetFocus();
MemoIn->Lines->Add(Socket->ReceiveText());
MemoOut->SetFocus();
}
//----------------------------------------------------------------------
Весь текст в листинге, расположенный до функции
1, создается автоматически.
Функция 1 представляет собой описание формы. В
тело этой функции добавим заголовок, который будет располагаться на форме.
Функции 2 и 4 являются реакциями на нажатие
кнопок ConnectBtn и DisconnectBtn.
Вам уже знаком метод заполнения в файле проекта подобных функций – двойной
щелчок на изображении соответствующей кнопки и можно записывать текст функции в
файл проекта.
Несколько пояснений к строкам, которые вы должны
записать в тело функции 2.
MySocket->Address = ServerEdit->Text;
MySocket->Port =
PortEdit->Text.ToIntDef(1971);
MySocket->Active = true;
ConnectButton->Enabled = false;
DisconnectButton->Enabled = true;
MemoOut->Enabled = true;
Эта функция настраивает компонент MySocket соответственно содержимому полей Server и Port экранной
формы, которые должны быть предварительно заполнены пользователем. Программа
присваивает свойству Address компонента
MySocket IP-адрес хоста. Метод ToIntDef(int)
класса AnsiString обеспечивает
установку порта по умолчанию в случае, если пользователь ввел в поле Port (элемент PortEdit)
неверно значение. После настройки свойств Address и Port программа инициирует подключение –
присваивает свойству Active компонента
MySocket значение True.Далее
блокируется кнопка ConnectButton, поскольку
подключение уже произошло, и разблокируется кнопка DisconnectButton.
Последняя операция – разблокирование компонента MemoOut,
что позволяет пользователю вводит
текст сообщения.
Обеспечив таким
образом запуск клиентского приложения, нужно включить в программу средства
прекращения сеанса связи с сервером. Для этого как раз и служит функция 4. Вот
основные строчки этой функции:
{
MySocket->Active = false;
ConnectButton->Enabled = true;
DisconnectButton->Enabled = false;
MemoOut->Enabled = false;
}
Функция разрыва соединения еще проще, чем
функция подключения. Она прерывает соединение с сервером, присваивая значение False свойству Active компонента MySocket,разблокирует
кнопку ConnectButton (это
позволяет пользователю при необходимости повторно подключиться к серверу),
блокирует кнопку DisconnectButton и
поле MemoOut.
Функции 3 и 6 служат для передачи имени
пользователя и других сообщений между сервером и несколькими клиентами. Чтобы
создать подобные функции в файле проекта, следует в окне Object Inspector выбрать события EventsèMySocetConnect (для функции 3) и EventsèMySocetRead (для функции 6).
Функция 3 после подключения
к серверу клиентского приложения должна передать информацию о том, кто вступает
в общий «разговор». Это делают следующие строчки функции 3.
{
MemoIn->SetFocus();
MemoIn->Lines->Add(“Connected
…”);
MemoOut->Socket->SendText(Format(“UsreName=%s”,
OPENARRY(TVarRec,
(UserNameEdit->Text))));
}
Сначала программа устанавливает фокус ввода
экранной формы на элемент управления MemoIN. Это
позволяет привлечь внимание пользователя к полю с сообщением о том, что
подключение к серверу состоялось. Текст этого сообщения вводится в поле MemoIn вторым оператором программы. После этого
фокус ввода переводится на элемент MemoOut, как
бы предлагая пользователю ввести в него текст сообщения. Последний оператор
программы передает введенный текст на сервер, причем сообщение форматируется
таким образом, что перед введенным в поле текстом вставляется “UserName=”.
Именно в таком виде сервер воспринимает сообщение, в котором
содержится имя только что подключившегося участника обсуждения. Благодаря этому
сообщению на сервере будет иметься информация о том, кого представляет данное
клиентское приложение.
Функция 5 служит для ввода текста. Вызвать на
экран для заполнения соответствующую функцию из файла проекта нужно выбрать
окно MemoOut, затем в Object Inspector
выбрать события OnKeyPressèMemoOutKeyPress.
После того как пользователь введет какой-либо
текст в поле MemoOut и
нажмет клавишу <Enter>,
сообщение передается на сервер. Это выполняется следующими
строчками функции 5:
if(Key
== VK_RETURN)
{
MySocket->Socket->SendText(MemoOut->Text);
MemoOut->Lines->Clear();
Key
= 0;
}
Функция проверяет, не было ли вызвано событие
нажатием клавиши с виртуальным кодом VK_RETURN. Если это так, то содержимое компонента MemoOut пересылается на сервер, а MemoOut очищается. Последний оператор программы сбрасывает
в нуль значение переменной Key; тем самым пользователю
разрешается начать новое сообщение.
Функция 6 заполняется в файл проекта точно также, как и функция 3. Эта функция служит для обработки
сообщений, поступивших от сервера. Эти действия выполняются следующими
строчками функции 6.
MemoIn->SetFocus();
MemoIn->Lines->Add(Socket->ReceiveText();
MemoOut->SetFocus();
Первый оператор этой программы устанавливает
фокус ввода экранной формы на компонент MemoIn,
тем самым пользователю предоставляется возможность
просматривать в режиме прокрутки текст, хранящийся в буфере этого компонента.
Затем в компонент MemoIn вводится
текст сообщения, полученного объектом Socket.
Последний оператор переносит фокус ввода на элемент MemoOut.
На этом разработка клиентского приложения
завершается. Выберите в меню команду Fileè Save All и сохраните файлы программы. Окно
готового слиентского приложения изображено на рис. 7.4.
Рис. 7.4.
Клиентское приложение
Для проверки функционирования разработанных
приложений выполните следующие операции.
·
Запустите на выполнение
сервер-приложение ChServer.
·
Настройте порт сервера
(по умолчанию используется порт 1971).
·
Щелкните на кнопке Старт в экранной форме приложения
сервера.
·
Запустите клиентское
приложение.
·
Установите тот же порт,
что и в приложении сервера.
·
Установите адрес сервера
127.0.0.1. Это – адрес локального хоста. Он
будет использоваться только тем компьютером, на котором вы сейчас работаете.
Введите какое-нибудь имя
пользователя в поле User Name и щелкните на кнопке Connect.
Рис. 7.5. Вариант
работы нескольких приложений
На рис. 7.5
изображен вариант, когда одновременно запущены в работу один сервер и три
клиентских приложения. На базе этих простейших программ можно потренироваться и
сделать попытку создания своей маленькой сети.
Протокол
почтовой службы Post
Office
Protocol (POP) и
простой протокол передачи почты Simple Main Transfer Protocol (SMTP) –
это два самых распространенных протокола, которые используются при создании
приложений для электронной почты. Протокол
POP используется
для получения сообщения по электронной почте от POP-серверов,
в протокол SMTP-для отсылки таких
сообщений через SMTP-сервер. В этом разделе
мы рассмотрим несложное приложение, которое позволяет посылать и принимать
сообщения по электронной почте.
Как всегда, сначала создаем
директорию для нового проекта, затем создаем новый проект. Для этого выберите в
меню FileèNewè Application.
В ответ C++ Builder создаст
пустую экранную форму Form1 и файл программного кода
Unit1.cpp. Сохраните
новый проект командой FileèSave Projekt As… в созданную заранее
директорию, при этом не будем назначать имена для формы и файла с исходными
кодами, а проекту дадим название Mail_POP3.bpr.
Теперь необходимо создать экранную форму в соответствии с изображением
на рис. 7.6.
Рис. 7.6.
Главная экранная форма проекта
Листинг
7.5. Файл Unit1.h
//----------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//----------------------------------------------------------------------
#include
<Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<NMpop3.hpp>
#include
<Psock.hpp>
#include
<Buttons.hpp>
#include <ComCtrls.hpp>
#include
<ExtCtrls.hpp>
#include
<NMsmtp.hpp>
//---------------------------------------------------------------------
class TForm1 : public
TForm
{
__published: // IDE-managed Components
TNMPOP3 *NMPOP31;
TBitBtn *CheckMail;
TPanel *Panel2;
TListView *ListView1;
TEdit *UserEdit;
TEdit *PasswordEdit;
TEdit *HostEdit;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
TButton *NewButton;
void __fastcall NMPOP31Connect(TObject
*Sender);
void __fastcall CheckMailClick(TObject
*Sender);
void __fastcall NMPOP31RetrieveEnd(TObject
*Sender);
void __fastcall ListView1DblClick(TObject
*Sender);
void __fastcall NewButtonClick(TObject
*Sender);
void __fastcall
NMPOP31AuthenticationFailed(bool &Handled);
private: // User declarations
int myId;
bool bSummary, bConnected;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------
extern PACKAGE TForm1
*Form1;
//---------------------------------------------------------------------
#endif
//---------------------------------------------------------------------
Мне думается, что вы уже
помните, что этот файл почти полностью создается средой программирования, за
исключением двух разделов. Эти разделы должен заполнять разработчик проекта.
q Включите
в экранную форму компонент TNMPOP3 (вы
найдете его на странице библиотеки FastNet).
Разместите его в
левом верхнем углу, как показано на
рис. 7.7.
q Добавьте
на поле экранной формы две кнопки Button. Первую назовите CheckMai1
и установите текст надписи на ней Получить почту а вторую назовите NewButton и
надпись на ней должна быть Новое письмо
Тексты надписей – это значения свойств Caption.
q Поместите на поле экранной формы компонент TListView,
который вы найдете на странице библиотеки Win32.
Свойству align нового элемента управления присвойте
значение alBottom и оставьте предлагаемое по умолчанию имя
компонента ListView1.
q Включите
в экранную форму три компонента TEdit и
назовите их UserEdit, PassWordEdit и HostEdit.
Свойству PasswordChar элемента
PasswordEdit присвойте значение * или любой другой
символ – именно он будет выводится в этом поле
вместо пароля при его вводе пользователем. Сейчас можно и назначить почтовый сервер по умолчанию
(например, <POP
сервер>) можно задать в качестве
значения свойства Text элемента
HostEdit.
q Добавьте
три компонента TLabel и
установите следующие значения свойств Caption для
них: Ваше имя, Пароль
и Server.
q Теперь
откройте редактор колонок –
дважды щелкните на поле компонента ListView1. Добавьте
в него две колонки: первая будет называться From (От), а
вторая – Subject (Содержание).
Присвойте свойству ViewStyle компонента ListView1 значение
vsReport.
После того как с
компоновкой экранной формы приложения будет в основном покончено, включите в
файл заголовка Unit1.h объявление нескольких общедоступных
переменных – добавьте в
раздел private: приведенные ниже операторы.
Bool bConnected, bSummary
Int myId
Сравните еще раз текст созданного вами файла
проекта Unit1.h с листингом 7.5 и
исправьте ошибки.
Для извлечения и просмотра
сообщений понадобится создать в приложении еще одну экранную форму. Выберите в
меню команду FileèNewèForm – в ответ среда выработки C++ Builder сформирует новую пустую
экранную форму и присвоит ей имя Form2,
а также новый модуль программного кода и присвоит ему имя Unit2.
Оставим все эти названия без изменения и установим на новую
форму компоненты в соответствии с рис. 7.7.
Рис. 7.7.
Форма для распечатки сообщений
В листинге 7.6 привожу текст файла Unit2.h.
Листинг 7.6.
Файл
Unit2.h
//----------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//----------------------------------------------------------------------
#include
<Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<ExtCtrls.hpp>
//----------------------------------------------------------------------
class TForm2 : public
TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
TMemo *MailMemo;
TLabel *Label1;
TLabel *Label2;
TLabel *FromLabel;
TLabel *SubjectLabel;
TButton *CloseButton;
void __fastcall CloseButtonClick(TObject
*Sender);
private: // User declarations
public: // User declarations
__fastcall TForm2(TComponent* Owner);
};
//----------------------------------------------------------------------
extern PACKAGE TForm2
*Form2;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
Устанавливайте на новую
экранную форму необходимые компоненты и сравнивайте их названия с записями в
листинге 7.6.
q В
верхнюю часть формы установим компонент Panel и присвоим значение alTop его свойству align.
Свойству Caption этого
компонента присвоим значение blank
q На
поверхности Panel1 расположим
четыре компонента TLabel, которым присвойте
наименования Label1, Label2, FromLabel,
SubjectLabel.
Свойствам Caption первых двух элементов надписей присвойте
следующие значения: элементу Label1 –
значение От кого:, а элементу Label2 – значение Заголовок:.
Для двух других элементов пока что оставьте те значения свойства Caption,
которые предлагаются по умолчанию средой разработки: FromLabel и SubjectLabel.
q Установим
на Panel1
кнопку (компонент TButton) и присвойте ей наименование CloseButton.
q Добавим в нижнюю часть экранной формы Form2 компонент TMemo и
присвоим его свойству align значение
alClient.
Свойству
name компонента TMemo присвойте значение Mai1Memo. На
этой стадии разработки экранная форма Form2 должна
выглядеть как на рис. 7.7.
Для того чтобы в этом диалоговом окне можно было
просмотреть содержимое поступивших сообщений, нужно
прежде всего включить модуль Unit2 в
главную экранную форму приложения (Form1).
Для выполнения этой процедуры нужно выполнить
следующие действия:
q вызовите
в окно редактора программного кода файл Unit1.cpp,выберите в меню File команду Include File Header, в открывшемся окне следует выбрать файл Unit2 и
нажать клавишу <Enter>.
Это позволит манипулировать копией формы Form2
из формы Form1.
Следующим этапом необходимо создать полноценные
файлы Unit1.cpp и Unit2.cpp, в
которых должны находиться исходные коды программы.
Сначала создадим файл Unit2.cpp, по образцу текста,
расположенного в листинге 7.7.
Листинг 7.7. Файл Unit2.cpp
//---------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include
"Unit2.h"
//---------------------------------------------------------------------
#pragma
package(smart_init)
#pragma resource
"*.dfm"
TForm2 *Form2;
//---------------------------------------------------------------------
__fastcall
TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------
void __fastcall
TForm2::CloseButtonClick(TObject *Sender)
{
Close();
}
//----------------------------------------------------------------------
Как видите, это очень
простой файл и никаких объяснений с моей стороны не требуется.
Перейдем к созданию файла Unit1.cpp с помощью
текста, записанного в листинге 7.8.
Листинг 7.8. Файл Unit1.cpp
//----------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include
"Unit1.h"
#include
"Unit2.h"
//----------------------------------------------------------------------
#pragma
package(smart_init)
#pragma resource
"*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------
__fastcall
TForm1::TForm1(TComponent* Owner)
// 1
: TForm(Owner)
{
}
//---------------------------------------------------------------------
void __fastcall
TForm1::NMPOP31Connect(TObject *Sender)
// 2
{
bConnected = true;
}
//----------------------------------------------------------------------
void __fastcall
TForm1::CheckMailClick(TObject *Sender)
// 3
{
bConnected = false;
NMPOP31->UserID = UserEdit->Text;
NMPOP31->Password =
PasswordEdit->Text;
NMPOP31->Host = HostEdit->Text;
NMPOP31->Connect();
if(NMPOP31->Connected)
{
if(NMPOP31->MailCount > 0)
{
bSummary = true;
for(int i = 0; i <
NMPOP31->MailCount; i++)
{
myId = i + 1;
NMPOP31->GetSummary(myId);
}
}
else
ShowMessage("Новых сообщений нет");
NMPOP31->Disconnect();
}
}
//---------------------------------------------------------------------
void __fastcall
TForm1::NMPOP31RetrieveEnd(TObject *Sender) // 4
{
if(bSummary)
{
TListItem *Temp =
ListView1->Items->Add();
Temp->Caption =
NMPOP31->Summary->From;
Temp->SubItems->Add(NMPOP31->Summary->Subject);
Temp->SubItems->Add(myId);
}
else
{
TForm2 *Temp = new TForm2(NULL);
Temp->MailMemo->Lines->Assign(NMPOP31->MailMessage->Body);
Temp->FromLabel->Caption =
NMPOP31->MailMessage->From;
Temp->SubjectLabel->Caption =
NMPOP31->MailMessage->Subject;
Temp->Show();
}
}
//----------------------------------------------------------------------
void __fastcall
TForm1::ListView1DblClick(TObject *Sender) // 5
{
NMPOP31->Connect();
if(ListView1->SelCount > 0)
{
bSummary = false;
NMPOP31->GetMailMessage(ListView1->Selected->SubItems->
Strings[1].ToIntDef(0));
}
NMPOP31->Disconnect();
}
//----------------------------------------------------------------------
void __fastcall
TForm1::NewButtonClick(TObject *Sender) // 6
{
ShowMessage("Эта кнопка не
работает");
}
//----------------------------------------------------------------------
void __fastcall
TForm1::NMPOP31AuthenticationFailed(bool &Handled) // 7
{
AnsiString NewPassword;
NewPassword = PasswordEdit->Text;
if(InputQuery("Пароль не верный",
"Введите новый пароль", NewPassword))
{
PasswordEdit->Text = NewPassword;
NMPOP31->Password = NewPassword;
Handled = true;
}
else
Handled = false;
}
//---------------------------------------------------------------------
Функция 1, как обычно,
описывает состояние формы Form1. Вы уже
знаете, что в теле этой функции можно задать различные значения для свойств компонентов, расположенных на
поверхности этой формы. Так мы очень часто поступали в проектах, описанных в главах 2 и 3. При создании своих программ поступайте так,
как считаете удобным.
Функция 2 реагирует на
двойной щелчок на событии OnConnectèNMPOP31Connect компонента NMPOP31.
Функция 3 является реакцией
на нажатие кнопки Получить почту. При работе с программой
перед нажатием этой кнопки следует вписать значения во все окна редактирования,
т.е. в программу должны быть введены:
q
ваше имя, которое
зарегистрировано на почтовом сервере как получатель почты;
q
пароль,
зарегистрированный на почтовом сервере;
q
«позывной»
почтового сервера, полученный вами от провайдера.
После нажатия этой кнопки сразу же
вызывается программа для выхода в Интернет и почтовый сервер. Если связь вашей
программы и почтового сервера установилась нормально, то на экран вашего
компьютера поочередно записываются заголовки всех сообщений электронной почты,
поступившие в ваш адрес. Если никаких сообщений нет, то программа выдает
диалоговое окно с текстом: «Никаких сообщений нет».
Функция 4 реагирует на
двойной щелчок на событии OnConnectèNMPOP31RetrieveEnd компонента NMPOP31. Эта функция наблюдает за порядком оформления
сообщения.
Функция 5 реагирует на двойной в поле компонента ListView1. Сначала
нужно присвоить значение True свойству
RowSelect списка ListView1 – тем самым пользователю будет позволено
выбирать элементы в списке с помощью щелчка мыши в любом месте соответствующей
строки, а не только на ее первом элементе. Далее добавьте приведенный в
листинге программный код в файл проекта.В этом
обработчике сначала проверяется, подключено ли приложение в текущий момент к
серверу. Если это не так, то вызывается метод Connect()
компонента NMPOP31 и
выполняется подключение. Далее выясняется, выбраны ли какие-либо элементы в
списке ListView;
таким способом предотвращается появление ошибки, которая
возникает в случае, если в момент выполнения двойного щелчка на поле элемента ListView в нем не было выбрано ни одно сообщение.
Если какой-либо элемент списка выбран, то глобальной переменной bSummary присваивается значение Falsr,
и тем самым остальные компоненты программы (в частности,
обработчик события RetrieveEnd объекта
NMPOP31)
извещаются о том, что извлекаться будет именно содержимое сообщений, а не
заголовок. В следующей строке вызывается метод GetMai1Message()
объекта NMPOP31. В
качестве аргумента методу передается идентификатор сообщения, с которым
пользователь пожелал ознакомиться. Этот идентификатор извлекается из второго
компонента (свойства SubItems) выбранного элемента списка. Метод ToIntDef()
класса AnsiString преобразует
строковое значение идентификатора в числовое.
Функция 6 выдает сообщение о том, что эта кнопка
не работает.
Функция 7 срабатывает
при проверке правильности введенного пароля.
Смысл (краткий) всех функций понятен, теперь
следует для каждой функции вызывать текстовый редактор с файлом проекта и
переписывать тексты из соответствующих функций листинга 7.8 в тела
соответствующих функций проектного файла Unit1.cpp.
Для проверки отсутствия ошибок вводим ProjectèMakeèMail_pop3. Если ошибок нет, то выполняем RunèRun и на экране должно появиться главое окно
программы, изображенное на рис. 7.8. Только учтите, что на этом рисунке
окно программы изображено после того, как была нажата клавиша
Получить почту. Перед началом этого эксперимента мною специально на почтовый
сервер были посланы три сообщения в мой же адрес. Программа прочитала с сервера
заголовки всех трех сообщений.
Рис. 7.8.
Главное окно программы с принятыми от сервера сообщениями
Если на любом из заголовков
этих сообщений сделать двойной щелчок, то на экране появляется окно с текстом
выбранного сообщения (см. рис. 7.9).
Рис. 7.9. Текст одного из сообщений
Только не пугайтесь
непонятным буквенным символам, расположенным на этом тексте. Это говорит о том,
что C++Builder плохо работает с буквами русского алфавита и если вы будете
использовать коды этой программы, то позаботьтесь о возможности декодирования
русских букв в различной кодировке.
Первый оператор в этой функции присваивает
общедоступному флагу – переменной bConnected –
значение false,
а затем устанавливаются значения свойств компонента NMPOP31.
Свойство UserID –
это имя пользователя, который обращается к услугам POP-сервера, свойство Password – пароль пользователя, Host – имя POP-сервера, например mai1.myserver.com.
После установки значений свойств
программа подключается к серверу, вызывая метод Connect()
компонента NMPOP31. Запросив
подключение, программа проверяет, установлено ли свойство Connected объекта класса TNMPOP3, т.е.
произошло ли подключение. Если дело обстоит именно так, то начинается обработка
списка сообщений, имеющихся для клиента на сервере. Сначала очищаются все
элементы объекта списка ListView1, а
затем общедоступному флагу
– переменной bSummary –
присваивается значение true.Тем самым обработчик
события CheckMai1Click извещает остальные компоненты приложения,
что считаны сведения о сообщениях, но не сами тексты сообщений.
Далее организуется цикл считывания заголовков
сообщений с сервера. Количество циклов равно текущему значению свойства Mai1Count компонента TNMPOP3. Первая
операция в теле цикла – присвоение глобальной переменной myId значения, равного увеличенному на 1
номеру цикла. Второй оператор в теле цикла вызывает метод GenSummary()
компонента TNMPOP3. Этот
метод извлекает текст резюме сообщения, номер которого соответствует значению
переменной myId,
а также возбуждает событие RetrieveEnd компонента TNMPOP3.
В случае, если на
сервере отсутствуют сообщения для подключившегося клиента, программа выводит
сообщение Новые сообщения отсутствуют.
Последняя операция в обработчике события CheckMai1Click – вызов
метода Disconnect()
объекта NMPOP31; этот
метод отсоединяет клиента от сервера.
Событие RetrieveEnd возбуждается
в двух случаях: когда появляется заголовок сообщения и когда извлекается само
сообщение. Уловить конкретную ситуацию помогает глобальная переменная-флаг bSummary:
этому флагу присваивается значение True,
когда выполняются операции с заголовком сообщений, и
значение False,
когда извлекаются сами сообщения. Если обрабатываются
заголовок сообщений, то программа добавляет очередной элемент в объект списка ListView1.
Для этого сначала создается переменная Temp класса TListItem и
приравнивается значению, возвращаемому
методом ListView1->Items->Add().
Таким образом создается новый
элемент списка ListView1. Далее
свойству Caption нового элемента списка ListItem присваивается значение отправителя
сообщения. Оно извлекается из свойства From объекта Summary в NMPOP31. Затем
в качестве компонентов элемента списка добавляются значения свойства Subject объекта Summary в NMPOP31 и
текущего ID сообщения (глобальная переменная myId).
После выполнения этих операций для всех сообщений в списке ListView1 будут
созданы элементы, каждый из которых представляет одно из сообщений, имеющихся
для пользователя на почтовом сервере. Манипулируя этим списком, пользователь
может выбрать те сообщения, с которыми желает познакомиться подробно (не
исключено, что часть сообщений пользователь отправить в корзину, не читая).
Класс TNMPOP3 располагает методом DeleteMailMessage(int Number), который удаляет
находящееся на сервере сообщение с указанным номером. Это позволяет уничтожать
неугодное сообщение, не выводя его на экран компьютера.
Для нового проекта создаем директорию, затем
создаем начальную часть проекта и сохраняем проект в этой директории. Этот
процесс вам хорошо известен, я не буду повторять порядок создания проекта.
Название формы и файла Unit1.cpp оставим без изменения,
а вот проекту дадим название Mail_smtp.bpr.
Нам необходимо создать экранную форму, в которой можно было бы
подготовить сообщение к отсылке, создать программный код для подключения к SMTP-серверу и для отсылки
серверу сформатированного сообщения.
Предлагаю вашему вниманию на рис. 7.10
подготовленную мною экранную форму для этого проекта.
Рис. 7.10.
Экранная форма для нового проекта
Рассмотрим порядок
установки компонентов на эту экранную форму.
q В верхней части формы располагаем
компонент TPanel, на котором будут
располагаться другие компоненты. Присвоим значение alTop его свойству align.
Свойству Caption этого
компонента присвойте значение blank;
q Всю
нижнюю часть формы заполним компонентом TMemo.
Присвоим его свойству align значение
alClient. Свойству
name компонента TMemo присвоим
значение MessageMemo.
q На
поверхности Panel1 расположим
пять компонентов TEdit, которым
присвойте наименования UserEdit,
HostEdit,
ToEdit,FromEdit и SubjectEdit. Во
всех элементах типа TEdit очистите
свойство Text.
q В состав
Panel1
включите еще и пять компонентов TLabel. Свойствам
Caption этих элементов присвойте значения Ваше
имя, Сервер SMTP, Кому:, От кого:, Заголовок:.
q Включите
в экранную форму компонент TNMSMTP (находится на странице библиотеки FastNet).
Оставьте значение его свойства Name таким,
как предлагается по умолчанию средой разработки C++ Builder, -- NMSMTP1.
q Добавьте
на поле экранной формы две кнопки (компонента TButton).
Первую назовите SehdButton, а надпись на ней
(свойство Caption)
должна быть Послать почту,
а вторую назовите CancelButton, а
надпись на ней, соответственно, Выход.
На этой стадии разработки экранная форма должна выглядеть
так, как показано на рис. 7.10.
Для контроля за правильностью
установки компонентов на форму предлагаю просмотреть файл Unit1.h, расположенный в
листинге 7.9.
Листинг 7.9.
Файл
Unit1.h
//---------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------
#include <Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<ExtCtrls.hpp>
#include
<NMsmtp.hpp>
#include
<Psock.hpp>
//---------------------------------------------------------------------
class TForm1 : public
TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
TMemo *MessageMemo;
TButton *Button1;
TButton *Button2;
TEdit *UserEdit;
TLabel *Label1;
TEdit *HostEdit;
TLabel *Label3;
TEdit *ToEdit;
TLabel *Label4;
TEdit *FromEdit;
TLabel *Label5;
TEdit *SubjectEdit;
TNMSMTP *NMSMTP1;
TLabel *Label6;
TLabel *Label2;
void __fastcall Button1Click(TObject
*Sender);
void __fastcall Button2Click(TObject
*Sender);
void __fastcall NMSMTP1Failure(TObject
*Sender);
void __fastcall NMSMTP1Success(TObject
*Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//----------------------------------------------------------------------
extern PACKAGE TForm1
*Form1;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
Никаких особенностей файл Unit1.h не имеет, поэтому
давать какие то пояснения не имеет смысла.
Далее приступим к созданию файла Unit1.cpp, в котором должны
находиться исходные коды проекта. В листинге 7.10 представлен текст файла Unit1.cpp, по образцу и подобию
которого мы должны создать файл с аналогичным названием в своем проекте.
Листинг
7.10. Файл
Unit1.cpp
//----------------------------------------------------------------------
#include
<vcl.h>
#pragma
hdrstop
#include
"Unit1.h"
//----------------------------------------------------------------------
#pragma
package(smart_init)
#pragma
resource "*.dfm"
TForm1
*Form1;
//----------------------------------------------------------------------
__fastcall
TForm1::TForm1(TComponent* Owner)
// 1
:
TForm(Owner)
{
Caption = "Послать
сообщение";
MessageMemo->Clear();
}
//----------------------------------------------------------------------
void
__fastcall TForm1::Button1Click(TObject *Sender) // 2
{
NMSMTP1->PostMessage->ToAddress->Clear();
NMSMTP1->PostMessage->ToAddress->CommaText = ToEdit->Text;
NMSMTP1->PostMessage->FromAddress =
FromEdit->Text;
NMSMTP1->PostMessage->ReplyTo =
FromEdit->Text;
NMSMTP1->PostMessage->Body->Assign(MessageMemo->Lines);
NMSMTP1->PostMessage->Subject =
SubjectEdit->Text;
NMSMTP1->PostMessage->LocalProgram =
"My Emailer";
if(NMSMTP1->Connected)
NMSMTP1->Disconnect();
NMSMTP1->UserID =
Form1->UserEdit->Text;
NMSMTP1->Host =
Form1->HostEdit->Text;
NMSMTP1->Connect();
NMSMTP1->SendMail();
}
//----------------------------------------------------------------------
void
__fastcall TForm1::Button2Click(TObject *Sender) // 3
{
Close();
}
//----------------------------------------------------------------------
void
__fastcall TForm1::NMSMTP1Failure(TObject *Sender) // 4
{
ShowMessage("Сообщение НЕ послано");
}
//---------------------------------------------------------------------
void
__fastcall TForm1::NMSMTP1Success(TObject *Sender) // 5
{
ShowMessage("Сообщение
послано");
Close();
}
//----------------------------------------------------------------------
Функция 1 описывает
форму. Здесь включена команда для
создания заголовка основному рабочему окну программы и команда на очищение MessageMemo.
Функция 2 является главной в этом проекте и её рассмотрим подробнее.
Сначала в этой функции подготавливается к работе
объект NMSMTP1.
Основным информационным компонентом в объекте класса TNMSMTP
является PostMessage,
в котором содержится вся информация, касающаяся сообщения, -- его тело и список рассылки (Send To).
В первой строке функции 2 подготавливается место
для нового адреса. Затем происходит заполнение PostMessage индивидуальными
данными, предназначенными для нового сообщения, т.е. устанавливаются компоненты
сообщения, предназначенного к отсылке. Первым делом установите значение
свойства ToAddress объекта PostMessage,
которое имеет тип TStringList и
содержит все адреса рассылки. С помощью свойства CommaText объекта ToAddress программа позволяет пользователю
назначить множество получателей отсылаемого сообщения в поле Кому:, причем в качестве разделителя
используется запятая или пробел. Далее свойству FromAddress присваивается значение, введенное ранее в
поле От кого:
(элемент управления FromEdit).
Последняя операция – перепись содержимого
сообщения из элемента экранной формы MessageMemo в
свойство Body объекта PostMessage.
Затем устанавливается значение свойства Subject –
в него переписывается текст из поля Заголовок:
(элемента управления SubjectEdit) экранной формы. В
объекте PostMessage остается после этого заполнить только
свойство LocalProgram;
в это поле можно записать все, что угодно, и обычно оно
используется при создании интегрированных программ работы с электронной почтой
для хранения маркера отсылки сообщения.
Установив в объекте PostMessage все параметры отсылаемого сообщения,
нужно заполнить и другие свойства объекта NMSMTP1, после
чего можно будет отослать сообщение. Сначала нужно проверить, не присоединен ли
объект NMSMTP1 к
серверу, и, если это так, то разорвать соединение. Далее нужно установить
значение свойств UserID и Host –
этим свойствам присваиваются значения, введенные в элементы управления главной
экранной формы приложения Form1. Последняя операция – подключение приложения к SMTP-серверу
и вызов метода SendMai1(), который и отвечает за
отсылку скомпонованного сообщения.
Функция 3 прекращает работу программы.
Функция 4 сообщает об ошибках, обнаруженных при
отправке почты.
Функция 5 сообщает об успешной отправке
почтового сообщения. Эти две функции являются реакцией компонента NMSMTP1 на события OnFailure и OnSuccess. Чтобы задействовать в
текстовом редакторе любую из этих функций, необходимо в окне Object Inspector
перейти на страницу Events и в строчке OnFailure на чистом поле следует
написать NMSMTP1Failure, а в строчке OnSuccess нужно написать NMSMTP1Success. Сразу после написания
любой из этих строчек нужно тут же перейти в окно редактора. Заготовка для
новой функции будет уже готова.
Итак, вами созданы все файлы нового проекта.
Теперь следует проверить на отсутствие ошибок и выполнить команду RunèRun. На
экране появится рабочее окно программы, изображенное на рис. 7.11.
Заполняйте окна редактирования необходимыми данными, создавайте текст пробного
сообщения и нажимайте кнопку Послать почту.
Рис. 7.11.
Главное окно программы с подготовленным сообщением
Если у вас все нормально,
то сообщение уйдет на сервер, а если будет допущена какая то ошибка, то
получите сообщение, как на рис. 7.12.
Рис. 7.12. Была допущена ошибка в адресации
На базе этой простенькой
программы можно сделать свою программу, более сложную. Обычно программу
отправки и получения почты объединяют, добавляют различные сервисные возможности и может получиться очень хорошая программа.
Многие полагают, что с
расширением сети Интернет и сферы применения протокола HTTP популярность протокола FTP – File
Transfer Prptpcol (Протокол передачи файлов) пошла на
убыль. Но это далеко не так. Просто большинство Web-браузеров имеет
встроенные средства работы с протоколом FTP, а
потому потребность в специализированных клиентских приложениях на базе этого
протокола действительно снизилась. Но есть еще множество приложений,
построенных исключительно на безе этого протокола, включая и приложения с
автоматическим обновлением компонентов.
В этом разделе мы рассмотрим процесс создания
простого клиентское приложения с использованием
протокола FTP на базе компонента TNMFTP,
который вы можете отыскать на странице библиотеки FastNET.
Как обычно, первый
шаг – создание директории для нового проекта. Затем выбираем в меню FileèNewèApplication. В ответ C++ Builder создаст
пустую экранную форму Form1 и файл программного
кода Unit1.cpp. Сохраните новый проект – выберите
в меню команду FileèSave Project As... При этом название Form1 и Unit1.cpp. оставляем без
изменений, а файл проекта назовем FTP.bpr.
Установку на форму различных компонентов будем
выполнять в соответствии с рис. 7.13.
Рис. 7.13.
Форма нового проекта
Далее привожу перечень компонентов,
установленных на форму.
q
В верхней
части панели располагаем компонент Panel и присваиваем значение alTop его свойству align. Свойству Caption этого компонента
присваиваем значение blank.
q Всю
нижнюю часть формы будет занимать компонент TListView, который вы найдете на
странице библиотеки Win32. Свойству align нового элемента
управления присваиваем значение alClient, а свойству Name – значение MyTree.
q Включаем
в экранную форму компонент TNMFTP,
который находится на вкладке FastNet, и назовем его MyFtp.
q Размещаем
на поле экранной формы компонент TImageList,
который находится на странице библиотеки
Win32.
Оставляем предлагаемое по умолчанию наименование этого элемента ImageList.
q Добавляем
на Panel1 три
компонента TLabel и установливаем для них
следующие значения свойств Caption: User, Password и Server.
q Также
добавляем три компонента TEdit, которым присвоим
наименования UserEdit, PasswordEdit и ServerEdit. Установите для этих
элементов управления значения по умолчанию: соответственно, anonymous, mail@mail.com и ftp.yourserver.com. Свойству PasswordChar элемента PasswordEdit необходимо присвоить
значение *.
q Добавляем
в Panel1 три
кнопки (компонента TButton) и назоваем их StartButton, StopButton и UploadButton. Надписи на кнопках
(значения свойств Caption)
должны быть соответственно Start, Stop и Upload.
q В
заключении установим на форму два компонента из страницы библиотеки Dialogs –
это компоненты OpenDialog и SaveDialog.
В результате всех этих операций экранная форма
нового приложения должна выглядеть примерно так, как на рис. 7.13.
Теперь можно добавить в элемент ImageList1 изображения. Дважды
щелкните на поле элемента ImageList1 и выберите Add. Далее можно
использовать изображения из папки Program Files\Common Files\Borland Shared\Images\Buttons и рекомендую добавить
из нее файлы fldropen.bmp и filenew.bmp. Поскольку эти
растровые изображения имеют размер 16х32 пикселей, а компонент Image настроен на размер
16х16, то появится диалоговое окно, в котором запрашивается, не согласны ли вы скорректировать размер изображения. Выберите Yes и удалите ненужную
часть изображения из компонента. Теперь у вас два изображения: одно
представляет папку (индекс этого изображения равен 0), а второе – файл
(индекс этого изображения равен 1).
Для контроля над работой по заполнению формы, в
листинге 7.11 привожу текст файла Unit1.h.
Листинг
7.11. Файл
Unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------
#include
<Classes.hpp>
#include
<Controls.hpp>
#include
<StdCtrls.hpp>
#include
<Forms.hpp>
#include
<NMFtp.hpp>
#include <Psock.hpp>
#include
<ComCtrls.hpp>
#include
<ImgList.hpp>
#include
<Dialogs.hpp>
#include
<ExtCtrls.hpp>
//---------------------------------------------------------------------
class TForm1 : public
TForm
{
__published: // IDE-managed Components
TNMFTP *MyFtp;
TTreeView *MyTree;
TImageList *ImageList1;
TOpenDialog *OpenDialog1;
TSaveDialog *SaveDialog;
TPanel *Panel1;
TButton *StartButton;
TEdit *Edit1;
TEdit *Edit2;
TEdit *Edit3;
TButton *StopButton;
TButton *UploadButton;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
void __fastcall StartButtonClick(TObject
*Sender);
void __fastcall MyTreeDblClick(TObject
*Sender);
void __fastcall MyTreeCompare(TObject
*Sender, TTreeNode *Node1,
TTreeNode *Node2, int Data, int
&Compare);
void __fastcall StopButtonClick(TObject
*Sender);
void __fastcall MyFtpFailure(bool
&Handled, TCmdType Trans_Type);
void __fastcall UploadButtonClick(TObject
*Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
void __fastcall DoList();
AnsiString __fastcall GetPath();
};
//----------------------------------------------------------------------
extern PACKAGE TForm1
*Form1;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
В секцию public: , расположенную в одноименном файле Unit1.h , находящемся в составе проекта, нужно переписать все из аналогичной
секции листинга 7.11. Файл из состава проекта должен точно соответствовать
файлу из листинга 7.11.
Сверим еще раз рис. 7.13 из книжки и
получившийся в процессе работы над формой проекта. Если все совпадает, то
переходим к работе с текстовым редактором.
В листинге 7.12 находится текст файла Unit1.cpp ,
по которому следует контролировать создание аналогичного файла в составе
проекта.
Листинг
7.12. Файл
Unit1.cpp
//----------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include
"Unit1.h"
//----------------------------------------------------------------------
#pragma
package(smart_init)
#pragma resource
"*.dfm"
TForm1 *Form1;
//----------------------------------------------------------------------
__fastcall
TForm1::TForm1(TComponent* Owner)
// 1
: TForm(Owner)
{
}
//----------------------------------------------------------------------
void __fastcall
TForm1::StartButtonClick(TObject *Sender)
// 2
{
MyFtp->Host = Edit3->Text;
MyFtp->UserID = Edit1->Text;
MyFtp->Password = Edit2->Text;
MyFtp->Connect();
StartButton->Enabled = false;
StopButton->Enabled = true;
MyTree->Items->Clear();
DoList();
}
//---------------------------------------------------------------------
void __fastcall
TForm1::DoList()
// 3
{
TTreeNode *Temp, *Root;
int i;
TCursor Save_Cursor = Screen->Cursor;
Screen->Cursor = crHourGlass; // курсор в виде песочных часов
Root = MyTree->Selected;
MyFtp->List();
MyTree->Items->BeginUpdate();
for(i=0;i<MyFtp->FTPDirectoryList->Attribute->Count;i++)
{
Temp =
MyTree->Items->AddChild(Root,MyFtp->FTPDirectoryList->
name->Strings[i]);
if((MyFtp->FTPDirectoryList->Attribute->Strings[i])[1]
== 'd')
{
// Папки
Temp->ImageIndex = 0;
Temp->SelectedIndex = 0;
}
else
{
// Файлы
Temp->ImageIndex = 1;
Temp->SelectedIndex = 1;
}
}
MyTree->AlphaSort();
MyTree->Items->EndUpdate();
if(Root)
Root->Expand(true);
Screen->Cursor = Save_Cursor;
}
//---------------------------------------------------------------------
void __fastcall
TForm1::MyTreeDblClick(TObject *Sender)
// 4
{
if(MyTree->Selected->ImageIndex == 0)
{
if(MyTree->Selected->Count == 0)
{
MyFtp->ChangeDir(GetPath());
DoList();
}
}
else
{
AnsiString RemoteFile;
RemoteFile = GetPath();
SaveDialog->FileName =
MyTree->Selected->Text;
if(SaveDialog->Execute())
MyFtp->Download(RemoteFile,
SaveDialog->FileName);
}
}
//---------------------------------------------------------------------
void __fastcall
TForm1::MyTreeCompare(TObject *Sender, TTreeNode *Node1,
TTreeNode *Node2, int Data, int
&Compare) // 5
{
if(Node1->ImageIndex >
Node2->ImageIndex)
Compare = 1;
else
if(Node1->ImageIndex ==
Node2->ImageIndex)
Compare =
CompareStr(Node1->Text, Node2->Text);
else
Compare = -1;
}
//----------------------------------------------------------------------
AnsiString __fastcall
TForm1::GetPath() // 6
{
TTreeNode *Base, *Temp;
TStringList *TempList = new TStringList();
int i;
AnsiString ToReturn;
Base = MyTree->Selected;
TempList->Add(Base->Text);
Temp = Base->Parent;
while(Temp)
{
TempList->Add(Temp->Text);
Temp = Temp->Parent;
}
for(i=TempList->Count-1;i>-1;i--)
{
ToReturn += "/" +
TempList->Strings[i];
}
return ToReturn;
}
//--------------------------------------------------------------------
void __fastcall
TForm1::StopButtonClick(TObject *Sender)
// 7
{
MyFtp->Disconnect();
StartButton->Enabled = true;
StopButton->Enabled = false;
}
//---------------------------------------------------------------------
void __fastcall
TForm1::MyFtpFailure(bool &Handled, TCmdType Trans_Type)
{
// 8
switch(Trans_Type)
{
case cmdChangeDir: ShowMessage("Неудачная смена директории");
case cmdMakeDir: ShowMessage("Неудачное создание директории");
case cmdDelete: ShowMessage("Неудачное удаление");
case cmdRemoveDir: ShowMessage("Неудачная замена директории");
case cmdList: ShowMessage("Неудачное чтение");
case cmdRename: ShowMessage("Неудачное переименование");
case cmdUpRestore: ShowMessage("Неудачное UploadRestore");
case cmdDownRestore: ShowMessage("Неудачное DownloadRestore");
case cmdDownload: ShowMessage("Неудачное скачивание");
case cmdUpload: ShowMessage("Неудачное Upload");
case cmdAppend: ShowMessage("Неудачное UploadAppend");
case cmdReInit: ShowMessage("Неудачная реинициализация");
case cmdAllocate: ShowMessage("Неудачное размещение");
case cmdNList: ShowMessage("Неудачное выполнение NList");
case cmdDoCommand: ShowMessage("Неудачное выполнение
DoCommand");
case cmdCurrentDir: ShowMessage("Неудачная текущая директория");
}
}
//---------------------------------------------------------------------
void __fastcall
TForm1::UploadButtonClick(TObject *Sender) // 9
{
if(OpenDialog1->Execute())
{
MyFtp->Upload(OpenDialog1->FileName,ExtractFileName(OpenDialog1->
FileName));
}
}
//----------------------------------------------------------------------
Функция 1 не
имеет каких либо доработок.
Функция 2
является реакцией на нажатие кнопки Start, которая начинает
работу приложения.
Работа начитается с установки свойства Host
объекта MyFtp –
этому свойству присваивается выбранный адрес сервера. Затем в объекте MyFtp
устанавливаются ID пользователя (свойство UserID) и
пароль (свойство Password). Довольно часто на
сайтах разрешается анонимный доступ, который позволяет задать имя
пользователя anonymous, а в качестве пароля –
адрес электронной почты. После этого выполняется подключение к серверу
(вызывается метод Connect() объекта MyFtp), блокируется кнопка Start и
разблокируется кнопка Stop. Далее очищается
контейнер Items объекта MyTree и
вызывается функция DoList(), которую мы рассмотрим
чуть позже.
Функция 3 будет
выводить на экран содержимое каталога на сервере. Присвойте свойству ParseList
компонента MyFtp значение True; это позволит сохранить
информацию о каталоге, которая содержится в объекте типа TFTPDirectoryList. Доступ к ней
открывается посредством свойства времени выполнения FTPDirectoryList
компонента TNMFTP. Далее, если вы этого
еще не сделали, добавьте в файл Unit1.h, в его секцию
общедоступных (public)
членов, объявление новой функции:
Void _fastcal1
DoList();
Затем в конец файла Unit1.cpp
перепишите полностью текст функции 3 из листинга 7.12.
Сначала в этой функции сохраняются параметры
текущего курсора – перед завершением выполнения функции их нужно будет
восстановить. Затем устанавливается новая форма курсора – песочные часы (hourglass). Такая форма курсора
общепринята, когда нужно сообщить пользователю, что приложение занято
выполнением достаточно продолжительной операции. Далее локальному объекту типа TTreeNode
присваивается указатель на выбранный элемент дерева MyTree. Если ни один элемент в
древовидном списке не выбран, значение указателя будет NULL,что и нужно нам в такой
ситуации. Далее вызывается метод List() объекта MyFtp. (Существует еще один
аналогичный метод, NList(),
который позволяет получить информацию только о наименованиях элементов, но для
данного приложения он не подходит.)
Вызов метода BeginUpdate() объекта класса TTreeNodes
(последний входит в состав MyTree) предотвращает обновление экрана до тех пор,
пока не будет вызван метод EndUpdate(). Благодаря этому
значительно повышается скорость вывода списка на экран. Затем организуется цикл
просмотра списка Attributes объекта класса TFTPDirectoryList элемента FTPDirectoryList. Поскольку список Attributes и
список имен в FTPDirectoryList соответствуют друг
другу, тот же цикл можно использовать и для обработки типов и имен элементов. В
теле цикла сначала создается новый объект класса TTreeNode, который становится
потомком текущего (если указатель на текущий равен NULL, то им является корень
дерева – root).
Надпись на каждом из новых элементов будет соответствовать содержимому
соответствующего элемента в списке имен. Затем проверяется первый символ в
элементе списка Attributes: если это – символ d, то объект является
папкой (или каталогом), и соответственно выбирается растр для
его отображения на поле древовидного списка (устанавливается значение индекса
0); в противном случае устанавливается значение индекса 1, а значит, выводится
изображение пиктограммы файла.
После завершения формирования в цикле списка
элементов он сортируется по алфавиту, и вызывается метод EndUpdate(), чем инициируется
обновление изображения на экране. Если перед обращением к функции в списке MyTree был
выбран некоторый элемент, то его ветвь в древовидной структуре раскрывается.
Последняя операция – восстановление
формы курсора.
Если же выбранный элемент является файлом, то
сначала в локальной переменной RemoteFile() фиксируется результат
выполнения метода GetPath(). Затем свойству FileName
объекта SaveDialog1
присваивается текст ярлыка выбранного файла (это – имя файла) и вызывается
метод Execute()
компонента SaveDialog1. Если работа с этим
диалоговым окном завершается щелчком на кнопке Cancel, никакие действия не
выполняются; в противном случае вызывается метод Download() объекта MyFtp. В качестве аргумента
этому методу передаются компоненты полного имени файла – значение
переменной RemoteFile() и свойство FileName
объекта диалогового окна SaveDialog1.
Функция 5 выполняет
сортировку списка файлов по типам, а затем – каждого типа по алфавиту.
Кроме того, эта функция будет определять полный путь для каждого узла
древовидной структуры. Сначала
включим в программу операцию установки свойства SortType
компонента MyTree –
этому свойству нужно присвоить значение stText. Текстовый редактор с
заготовкой под аналогичную функцию вызывается через событие OnCompare
компонента MyTree. В тела заготовки добавьте программный код из функции 5, расположенной в
листинге 7.12. В этой функции сначала
анализируются значения свойств ImageIndex
сравниваемых узлов, содержащих информацию о типе узла – папки или файлы. Если
значение ImageIndex узла Node1 больше, то, значит,
этот узел представляет в списке файл, и переменной Compare
присваивается значение 1. В
противном случае проверяется, не равны ли значения индексов – в этом
случае оба узла представляют либо файлы, либо папки. В случае равенства
вызывается функция CompareStr(), которой в качестве
аргумента передается текст ярлыка каждого узла. Если же индексы не равны, т.е.
индекс узла Node2 больше индекса узла Node1, а значит, узел Node1 представляет папку, а
узел Node2 – файл, то переменной Compare
присваивается значение -1.
После того как пользователь дважды щелкнет на
пиктограмме какого-либо узла на поле списка MyList,
должна быть выполнена одна из трех операций:
q Если
щелчок выполнен на пиктограмме файла, то должна начаться перегрузка выбранного
файла;
q Если
щелчок выполнен на пиктограмме папки, содержимое которой еще не включено в
список, то нужно загрузить в список перечень компонентов из этой папки;
q Если
щелчок выполнен на пиктограмме папки, содержимое которой уже включено в список,
то соответствующая ветвь списка должна быть развернута.
Третий вариант процедуры выполняется в объекте
класса TTreeView
по умолчанию, и для ее реализации в программу не нужно
добавлять специальный код.
Функция 6 представляет
собой метод GetPath() и играет важную роль во
всех операциях с каталогами и файлами на сервере. Поскольку дерево каталогов
наследуется элементами объекта списка MyList, этот метод нужно
использовать для определения пути к выбранным папкам или файлам. Текст этой
функции из листинга 7.12 нужно полностью и аккуратно скопировать в файл Unit1.cpp, расположенный в
создаваемом проекте. Если вы не сделали это раньше, то добавьте в секцию
общедоступных объектов файла Unit1.h следующий оператор объявления:
AnsiString _fastcal1
GetPath().
В этой функции (функция 6) сначала создается
пара локальных переменных типа TTreeNode и
новый список типа TStringList. В переменную Base
дублируется указатель на выбранный узел типа TTreeNode
объекта MyList.
Затем текст ярлыка этого узла добавляется в список TempList, а в переменную Temp
записывается указатель на родительский узел того узла, который зафиксирован в Base. Далее организуется цикл
перехода по цепочке родительских узлов, который выполняется до тех пор, пока мы
не выйдем на корневой узел дерева, т.е. узел, не имеющий родительского узла.
При каждом переходе «вверх» по дереву в список TempList
добавляется очередной ярлык. После завершения цикла просмотра дерева
заполненные ярлыки считываются из списка TempList в
обратном порядке, и формируется строка полного пути, в которой разделителем
является косая черта. Перед завершением программы список TempList
удаляется, а в вызывающую программу возвращается сформированная строка.
Функция 8 выдает
сообщения об ошибках, обнаруженных при выполнении каких то действий программы.
Текст этой функции из листинга 7.12 нужно полностью переписать в файл Unit1.cpp, расположенный в нашем
проекте.
Функция 9 служит
для того, чтобы программа могла
пересылать выбранный файл. Она является реакцией на нажатие кнопки UploadButton. В этой простенькой
функции вызывается метод Execute объекта диалогового
окна OpenDialog1.
Если работа с диалоговым окном завершена успешно (пользователь не щелкал на
кнопке Cancel), вызывается метод Upload() компонента MyFtp. В качестве аргументов
этому методу передаются полное имя файла (включая и путь) и имя файла без пути.
Теперь работа над проектом завершена, и можно
сохранить все файлы приложения. Не забудьте убедиться в отсутствии ошибок при
работе в текстовом редакторе с файлами Unit1.h и Unit1.cpp. После выполнения
команды RunèRun вы
получите на экране главное окно программы. Заполняйте необходимые данные и
нажимайте кнопку Start. То, что получилось после этих действий на экране моего компьютера вы
сможете увидеть на рис. 7.14 и рис. 7.15.
Рис. 7.14. Главное окно приложения с информацией
Рис. 7.15. То же окно после просмотра информации
Стоит заметить, что несмотря на кажущуюся простоту исходных кодов программы,
работу свою она выполняет безупречно.
В этой главе вы научились
расширять возможности создаваемых приложений и обеспечивать обмен информацией
по сети. Вы имели возможность на реальных примерах изучить методику программирования
различных сетевых протоколов. Теперь в вашем арсенале появились инструменты,
которые позволят создавать эффективные приложения для работы в среде Интернета.
Не упускайте такой возможности!