Delphi: обмен данными между приложениями

КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"

[Главная страница] [Delphi] [Контакты]

Сообщение WM_COPYDATA


Организация обмена данными между приложениями, а именно между процессами этих приложений, является достаточно трудоемкой задачей. Архитектура Win32 подразумевает максимальную изоляцию выполняющихся приложений друг от друга. Каждое приложение исполняется в своем виртуальном адресном пространстве, которое изолировано и не имеет доступа к памяти других процессов приложений. Но довольно часто возникает необходимость передачи данных из одного выполняющегося процесса в другой. Это вызвано тем, что функциональные приложения и пакеты программ исполняются не в одном процессе, поэтому для нормальной работы используются основные возможности межпроцессного взаимодействия. Наиболее простым, понятным, но не всегда удобным является передача данных с использованием сообщения WMCOPYDATA. Также для передачи данных между приложениями широко используются проецируемые в память файлы (Mapping Files). Существуют и такие высокоуровневые средства, как буфер обмена или технология СОМ.

Сообщение WM_COPYDATA позволяет приложениям копировать данные между их адресными пространствами. Для передачи сообщения должна использоваться функция синхронной отправки сообщения SendMessage, а не PostMessage, которая асинхронным образом передает сообщение. Данные, предназначенные для передачи, не должны содержать указателей или других ссылок на объекты, недоступные для программы, принимающей эти данные.

Итак, приступим к созданию приложения, демонстрирующего работу WM_COPYDATA. Для создания хорошего примера потребуется создать два приложения. Первое будет отправлять данные (например, строку текста), другое приложение будет их получать. На главной форме первого приложения помещаем элемент управления TextBox, в который будет записываться передаваемая строка, и кнопку, нажатие которой инициирует передачу данных. Для второго приложения достаточно элемента для отображения текстовой информации типа Label. Перейдем к рассмотрению исходных текстов созданных приложений.

Рис. 1. Вид приложений посылки и получения строки

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

const
  CMD_SETLABELTEXT = 1; //Задаем ID команды

На форме находится кнопка отправки данных другому приложению, ее обработчик выглядит еледующим образом (листинг 1).

Листинг 1. Отправка данных другому приложению


procedure TForm1.btnSendClick(Sender: TObject);
var
  CDS: TCopyDataStruct;
begin
  //Устанавливаем тип команды
  CDS.dwData := CMD_SETLABELTEXT;
  //Устанавливаем длину передаваемых данных
  CDS.cbData := Length(StringEdit.Text) + 1;
  //Выделяем память буфера для передачи данных
  GetMem(CDS.lpData, CDS.cbData);
  try
    //Копируем данные в буфер
    StrPCopy(CDS.lpData, AnsiString(StringEdit.Text));
    //Отсылаем сообщение в окно с заголовком StringReceiver
    SendMessage(FindWindow(nil, 'StringReceiver'),
                  WM_COPYDATA, Handle, Integer(@CDS));
  finally
    //Высвобождаем буфер
    FreeMem(CDS.lpData, CDS.cbData);
  end;
end;

Подробного комментария данный листинг не требует. Обратите лишь внимание на вызов функции SendMessage, которая использует FindWindow для задания одного из своих параметров. Процедура FindWindow в случае успешного выполнения возвращает HWND окна, заголовок которого задается в параметре этой функции (строка StringReciever из предыдущего примера). Синхронная отправка сообщения VM_COPYDATA с набором данных, которые помещены в структуру CDS, осуществляется вызовом SendMessage.

Рассмотрим второе приложение, которое принимает строку и отображает ее в надписи (Label). Для начала в блок объявления помещаем обработчик сообщения и объявляем само сообщение WM_COPYDATA:

type
  TForm1 = class(TForm)
    LabelStr: TLabel;
  private
    //Обработчик сообщения WM_COPYDATA
    procedure WMCopyData(var MessageData: TWMCopyData); message WM_COPYDATA;

Как и в случае первого приложения, нам необходима константа, которая будет идентифицировать тип операции:

const
  CMD_SETLABELTEXT = 1;

Далее рассмотрим тело функции обработчика сообщения WM_COPYDATA (листинг 2).

Листинг 2. Обработка сообщения WM_COPYDATA


procedure TForm1.WMCopyData(var MessageData: TWMCopyData);
begin
  //Устанавливаем свойства метки, если заданная команда совпадает
  if MessageData.CopyDataStruct.dwData = CMD_SETLABELTEXT then
  begin
    //Устанавливаем текст из полученных данных
    LabelStr.Caption := PAnsiChar((MessageData.CopyDataStruct.lpData));
    MessageData.Result := 1;
  end
  else
    MessageData.Result := 0;
end;

 Если окну второго приложения, которое носит название StringReciver (получатель строки), приходит сообщение WM_COPYDATA, то происходит вызов WMCopyData. В качестве параметра эта процедура получает структуру данных МеssageData типа TWMCopyData, содержащую идентификатор операции и данные (передаваемую строку). После проверки типа операции в случае совпадения его с константой CMD_SETLABELTEXT полученные данные преобразуются в строку. Преобразование происходит при помощи функции PAnsiChar. Полученная строка устанавливается в качестве заголовка для меткие именем LabelStr. Затем полю Result структуры MessageData присваивается значение 1 или 0, в зависимости от успеха операции.

Таким образом, для передачи данных (строки) записываем передаваемую строку в текстовое поле первой формы и нажимаем кнопку Отправить. Результат работы приложений можно увидеть на рис. 1.

Необходимо добавить, что передача данных посредством сообщения WM_COPYDATA является удобным и простым способом. Но WM_COPYDATA можно передавать только функцией SendMessage, и это является существенным недостатком такого метода. SendMessage «замораживаете работу приложения-отправителя, поэтому такой способ применяется для передачи небольших объемов данных, которые не требуют сложной обработки на стороне программы-приемника. К тому же на использование WM_COPYDATA налагаются некоторые существенные ограничения, о которых говорилось выше.

Исходный код проекта здесь. Выполнен на Delphi XE.