КОМПЬЮТЕРНЫЕ КУРСЫ "ПОИСК"
[Главная страница] [Delphi] [Контакты]

Уделим немного времени вопросу создания собственного формата буфера обмена. Предположим, что в разрабатываемой базе данных в одну из таблиц вводится следующая однотипная информация: наименование товара, производитель, единица измерения, дата поступления и цена. Зачастую пользователю приходится вводить порядка десятка практически одинаковых записей, отличающихся лишь ценой или датой.
Чтобы облегчить его труд, научим нашу программу реализовывать методы копирования в буфер и вставки из буфера структурированных данных – записей. Однако в 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. Профессиональное программирование. Дмитрий Осипов.