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

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

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

Создание собственного формата буфера обмена


obmen-5

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

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

unit My_CF;

interface
uses Windows, Clipbrd;

type
  TDBRecord = packed record //тип записи, повторяющий структуру строки таблицы базы
    Name,Creator: string[40];  //товар, производитель
    Measure: string[5];        //единица измерения
    Money: currency;           //цена за единицу
    Date: TDateTime;           //дата поступления
  end;

  // объявление методов для работы с буфером
  procedure CopyToClipboard(DBRecord: TDBRecord);
  function LoadFromClipboard: TDBRecord;

const
  FormatName='CF_MyFormat'; // константа с названием пользовательского
                            // формата буфера
var
  CF_MyFormat : Word; // переменная для хранения идентификатора формата

implementation

procedure CopyToClipboard(DBRecord : TDBRecord);
begin
  //операции копирования данных DBRecord : TDBRecord в буфер обмена
end;

function LoadFromClipboard : TDBRecord;
begin
  //операции изъятия данных из буфера обмена
end;  

Первое, что происходит после старта модуля, – это регистрация нашего пользовательского формата данных. Для этого в секции инициализации вызывается метод RegisterClipboardFormat():

function RegisterClipboardFormat(szFormat: PChar) : Integer;

Функцию регистрации формата интересует один-единственный параметр, содержащий название нового формата. В случае успешной регистрации метод возвращает число, идентифицирующее новый формат буфера обмена. Полученное значение будет находиться в диапазоне от 0xC000 до 0xFFFF. С этого момента формат станет доступным в списке форматов буфера обмена (вспомните методы HasFormat и Formats).

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

Вернемся к нашему примеру. Для хранения данных, соответствующих одной строке таблицы базы данных, в листинге объявлена запись TDBRecord, чья структура полностью повторяет характеристики полей таблицы. Далее объявлены заготовки методов, специализирующихся на операциях копирования строки из таблицы в буфер обмена (метод CopyToClipboard) и вставки строки из буфера (LoadFromClipboard). Настала пора наполнить эти методы содержанием. Взгляните на листинг процедуры, осуществляющей копирование данных в буфер обмена:

procedure CopyToClipboard(DBRecord : TDBRecord);
var
  h: THandle;
  FirstByte: Pointer;
begin
  //операции копирования данных DBRecord : TDBRecord в буфер обмена
  h := GlobalAlloc(GMEM_MOVEABLE, SizeOf(TDBRecord)); //распределяем область памяти
  FirstByte := GlobalLock(h);                       //начало зарезервированной области
  try
    Move(DBRecord, FirstByte^, SizeOf(TDBRecord));  //переносим DBRecord в память
    Clipboard.SetAsHandle(CF_MyFormat, h);          //указываем буферу на данные
  finally
    GlobalUnLock(h); //освобождаем зарезервированную память
  end;
end;

Метод GlobalAlloc() в общей куче распределяет некую область памяти, необходимую для размещения в ней содержимого записи TDBRecord. В первом параметре метода (при помощи константы GMEM_MOVEABLE) мы указываем, что данная область памяти может быть перемещаемой, а во втором параметре определяем размер этой области в байтах. Метод возвращает дескриптор созданного объекта, а мы в свою очередь сохраняем его в переменной h.

Следующий метод GlobalLock(), получив дескриптор h только что сформированного объекта, транслирует его в физический адрес этого объекта – адрес его первого байта в области памяти. Помимо этого метод GlobalLock информирует операционную систему о том, что мы приступили к работе с объектом h. Физически это выглядит как приращение счетчика ссылок на объект на единицу.

Процедура Move() из арсенала методов Delphi (модуль System) перемещает содержимое DBRecord в зарезервированную область памяти в байт с адресом FirstByte^. Нам остается воспользоваться уже знакомой процедурой SetAsHandle() из коллекции методов класса TClipboard. В заключение в секции finally с помощью метода GlobalUnLock() мы уменьшаем на единицу счетчик ссылок на эту область памяти, тем самым информируя Windows о том, что приложение прекратило работу с объектом h.

Рассмотрим метод, извлекающий данные из буфера обмена.

function LoadFromClipboard : TDBRecord;
var
  h: THandle;
  FirstByte: Pointer;
begin
  //операции изъятия данных из буфера обмена
  h := Clipboard.GetAsHandle(CF_MyFormat);
  if h <> 0 then
  begin
    FirstByte := GlobalLock(h);
    try
      Move(FirstByte^, Result, SizeOf(TDBRecord));
    finally
      GlobalUnLock(h);
    end;
  end;
end;

С помощью метода GetAsHandle() в переменную h записывается указатель буфера обмена. Если h не равен нулю (признак того, что буфер не пуст), узнаем адрес первого байта области памяти буфера и при помощи метода Move() копируем данные из буфера в структуру TDBRecord. Для того чтобы проверить работоспособность кода, на главной форме создаем две кнопки и пишем к ним две событийные процедуры реагирующие на щелчок мышью.

implementation

uses My_CF;

{$R *.dfm}

procedure TfrmMain.btnCopyToClipboardClick(Sender: TObject);
var
  DBRecord: TDBRecord;
begin
  DBRecord.Name := edName.Text;
  DBRecord.Creator := edCreator.Text;
  DBRecord.Measure := edMeasure.Text;
  DBRecord.Money := StrToFloat(edMoney.Text);
  DBRecord.Date := dtpDate.Date;
  CopyToClipboard(DBRecord);
end;

procedure TfrmMain.btnPasteFromClipboardClick(Sender: TObject);
var
  DBRecord: TDBRecord;
begin
  DBRecord := LoadFromClipboard;
  edName1.Text := DBRecord.Name;
  edCreator1.Text := DBRecord.Creator;
  edMeasure1.Text := DBRecord.Measure;
  edMoney1.Text := FloatToStr(DBRecord.Money);
  edDate.Text := DateToStr(DBRecord.Date);
end;

Исходный код проекта (Delphi XE)

Используемая литература: Delphi. Профессиональное программирование. Дмитрий Осипов.