DDE-Чат

В очередной своей работе я хочу рассказать о DDE - Dynamic Data Exchange, то есть о динамическом обмене данными между приложениями. Начав изучать тему DDE, я понял, что это как-то похоже на чат. Принцип работы с DDE - это технология Клиент-Сервер. Delphi позволяет создавать как сервер, так и клиент. Все компоненты для организации технологии DDE находятся на странице System Палитры компонентов. Сразу хочу отметить, что при использовании DDE возникает множество проблем, которые будут оговорены далее.

Приложение Сервера:

Для начала попробуем создать приложение сервера. Для этого нужно создать новый проект. Затем положить на форму стандартные и специальные компоненты присущие Серверу:
  • 1. TDdeServerConv;
  • 2. TDdeServerItem;
  • 3. Два компонента TMemo для отправляемых и принимаемых сообщений;
  • 4. Кнопку TButton.

Компонент TDdeServerConv представляет собой сеанс передачи данных, а компонент TDdeServerItem - сами передаваемые данные. Обычно приложение Сервера содержит один компонент TDdeServerConv и несколько компонентов TDdeServerItem. Эти компоненты используются совместно. Через свойство ServerConv компонент TDdeServerItem связывается с TDdeServerConv.

...
DdeServerItem1.ServerConv:=DdeServerConv1;
...

Так как TDdeServerItem содержит сами передаваемые данные, то эти данные должны где-то содержаться. Все данные заносятся в два основных свойства: Lines - для передачи сразу нескольких строк и Text - для передачи одной строки. Как только одно из этих свойств изменяться, они сразу же передаются приложению Клиента.

Для того чтобы организовать соединение Клиента с Сервером, Клиенту нужно знать все параметры Сервера. Для этого нужно использовать метод CopyToClipboard;

...
DdeServerItem1.CopyToClipboard;
...

При получении данных от клиента нужно обрабатывать событие onPokeData. В нашем случае нужно просто отобразить переданные данные на одном из компонентов TMemo.

procedure TForm1.DdeServerItem1PokeData(Sender: TObject);
begin
 ServerMemoIn.Lines:=DdeServerItem1.Lines;
end;

А теперь рассмотрим наглядный пример приложения Сервера. Начнем с процедуры создания формы:

procedure TForm1.FormCreate(Sender: TObject);
begin
 ServerMemoOut.Clear; // Компонент для передаваемых данных
 ServerMemoIn.Clear; // Компонент для получаемых данных

 DdeServerItem1.CopyToClipboard; // Сохраняем параметры Сервера
 DdeServerItem1.ServerConv:=DdeServerConv1;
end;

При получении данных от Клиента отображаемых их в одном из компонентов TMemo:

procedure TForm1.DdeServerItem1PokeData(Sender: TObject);
begin
 ServerMemoIn.Lines:=DdeServerItem1.Lines;
end;

Кнопка для отправки сообщения Клиенту. При нажатии на кнопку происходи присвоение текста, введенного в другой компонент TMemo и его немедленная отправка Клиенту:

procedure TForm1.btnSendClick(Sender: TObject);
begin
 DdeServerItem1.Lines:=ServerMemoOut.Lines;
end;

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

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var H: HWND;
begin
 H:=FindWindow(Nil, 'ChatClient');
 If H <> 0 Then SendMessage(H, WM_CLOSE, 0, 0);
end;

Trouble № 1:   Мною была замечена одна очень не приятная вещь. Приложение Сервера без проблем принимает многострочные данные от Клиента, а в свою очередь Клиент не "хочет" принимать многострочные данных от Сервера. Именно поэтому я решил запретить нажатие клавиши Enter, чтобы хоть как-то скрыть этот огромный недостаток.

procedure TForm1.ServerMemoOutKeyPress(Sender: TObject; var Key: Char);
begin
 If Key = #13 Then Key:=#0; 
end;

Приложение Клиента:

Теперь попробуем создать приложение Клиента. Клиент предназначен для получения данных от приложения Сервера. Теоретически Клиентов может быть несколько. Создадим новый проект. Затем положить на форму стандартные и специальные компоненты присущие Клиенту:

  • 1. TDdeClientConv;
  • 2. TDdeClientItem;
  • 3. Два компонента TMemo для отправляемых и принимаемых сообщений;
  • 4. Кнопку TButton.

Компонент TDdeClientConv также представляет собой сеанс передачи данных, а компонент TDdeClientItem - сами передаваемые данные, которые также хранятся в свойствах Lines - для отправки нескольких строк и Text - для отправки одной строки. Обычно приложение клиента также содержит один компонент TDdeClientConv и несколько компонентов TDdeClientItem. Эти компоненты используются совместно. Через свойство DdeConv компонент TDdeClientItem связывается с компонентом TDdeClientConv. А через свойство DdeItem задается источник данных.

...
DdeClientItem1.DdeConv:=DdeClientConv1;
DdeClientItem1.DdeItem:='DdeServerItem1';
...

Для организации параметров соединения используются следующие свойства:

  • DdeService - Имя Сервера, если Сервер располагается в текущей директории;
  • DdeTopic - Тема обмена, обычно это компонент TDdeServerConv
  • ServiceApplication - Имя Сервера, если Сервер располагается не в текущей директории.

Имя Сервера принято указывать без расширения, хотя даже если и указать расширение, то это не вызовет ни какой ошибки.

Trouble № 2:    Установить перечисленные свойства динамически, то есть во время выполнения программы, скорее всего не получиться. Все эти команды будут просто игнорированы. Это нужно делать на этапе конструирования формы через Инспектор объектов.

...
DdeClientConv1.DdeService:='ChatServer';
DdeClientConv1.DdeTopic:='DdeServerConv1';
...
// ИЛИ
...
DdeClientConv1.ServiceApplication:='С:\ChatServer';
DdeClientConv1.DdeTopic:='DdeServerConv1';
...

При установки свойств через Инспектор объектов нужно воспользоваться одним из свойств компонента TDdeClientConv: DdeService или DdeTopic. При этом заполнение полей одного свойства приведет к заполнению полей другого свойства. Также можно нажать кнопку "Paste Link", что приведет к автоматическому заполнению полей. Но, эта кнопка будет активна только в том случае, если приложение Сервера было хоть раз запущено ранее, то есть "сработал" метод CopyToClipboard.

При помощи свойства ConnectMode можно управлять способ подключения к Серверу. Если это свойство установлено в ddeAutomatic, то подключение осуществляется автоматически, а если свойство установлено в ddeManual, то подключение осуществляется вручную и для нормальной работы приложения программисту придется самому описывать все операторы. Для этого нужно воспользоваться группой операторов SetLink, OpenLink, CloseLink и RequestData.

Для того чтобы отправить текстовое сообщение на Сервер существуют две основные функции PokeData - для отправки на Сервер однострочных данных и функция PokeDataLines для отправки многострочных данных.

А теперь рассмотрим наглядный пример приложения Клиента. Начнем с процедуры создания формы:

procedure TForm1.FormCreate(Sender: TObject);
begin
 ClientMemoOut.Clear;
 ClientMemoIn.Clear;

 DdeClientItem1.DdeConv:=DdeClientConv1;
 DdeClientItem1.DdeItem:='DdeServerItem1';

 DdeClientConv1.DdeService:='ChatServer';
 DdeClientConv1.DdeTopic:='DdeServerConv1';
end;

Кнопка для отправки данных на Сервер. Данные отправляются двумя различными способами. При отправке данных первым способ используется функция Trim для удаления не нужных пробелов, потому что при получении данных эти пробелы могут быть интерпретированы как кавычки или любые другие символы:

procedure TForm1.btnSendClick(Sender: TObject);
begin
 DdeClientConv1.PokeData(DdeClientItem1.DdeItem, PChar(Trim(ClientMemoOut.Text)));
 DdeClientConv2.PokeDataLines(DdeClientItem2.DdeItem, ClientMemoOut.Lines);
end;

Процедура для отображения полученных данных от Сервера:

procedure TForm1.DdeClientItem1Change(Sender: TObject);
begin
 ClientMemoIn.Lines:=DdeClientItem1.Lines;
end;

При закрытии приложения Клиента также закрываем и приложение Сервера:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
Var H: HWND;
begin
 H:=FindWindow(Nil, 'ChatServer');
 If H <> 0 Then SendMessage(H, WM_CLOSE, 0, 0);
end;

Здесь также запрещаем использовании клавиши Enter:

procedure TForm1.ClientMemoOutKeyPress(Sender: TObject; var Key: Char);
begin
 If Key = #13 Then Key:=#0;
end;

В процессе написания и отладки программы иногда необходимо узнать некоторые данные о соединении. Так, например, можно узнать имя Сервера:

...
ShowMessage('Имя сервера 1 способом ' + DdeClientItem1.DdeConv.DdeService);
ShowMessage('Имя сервера 2 способом ' + DdeClientConv1.DdeService);
...

А так можно узнать тему обмена данными и DdeServerItem:

...
ShowMessage('Тема ' + DdeClientConv1.DdeTopic);
ShowMessage('DdeServerItem ' + DdeClientItem1.DdeItem);
...

Вернуться в оглавление
Вернуться на главную страницу
Hosted by uCoz