DDE.
Думаю, вы уже заметили, что один из переключателей вариантов имеет название, состоящее из зловещего сочетания букв - DDE. Первое, что вы можете почувствовать при виде этих букв, - это застоялый запах плесени и старения. Да, DDE - пережиток старого доброго времени (а, кажется, это было совсем недавно - “как молоды мы были”), времени DOS-резидентов, командной строки, капитана Нортона и первых (не смотря на их номер - 3.0) “форточек”. Черт, тут жизнь уже проходит, а DDE все еще жив. И слава Богу! Ведь, мы решим “главную” проблему - сохранение содержимого буфера обмена. Вот так:
procedure TForm1.ToDDE(ISheet: IxlWorksheet);
var xlDDE: TDDEClientConv;
i: integer;
IR1, IR2, IRange: IxlRange;
Buff: string;
begin
Buff := ”;
tblCust.First;
while not tblCust.EOF do begin
for i := 0 to tblCust.Fields.Count - 1 do begin
Buff := Buff + FieldToStr(tblCust.Fields[i]);
if i < (tblCust.Fields.Count - 1) then
Buff := Buff + #9;
end;
tblCust.Next;
if not tblCust.EOF then
Buff := Buff + #10;
end;
IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount - 1,
StartColumn + tblCust.Fields.Count - 1];
IRange := ISheet.Range[IR1, IR2];
xlDDE := TDDEClientConv.Create(Self);
try
if xlDDE.SetLink(’EXCEL’, ISheet.Name) then
xlDDE.PokeData(OLEVariant(IRange).Address[ReferenceStyle:=xlR1C1], PChar(Buff));
finally
xlDDE.Free;
end;
end;
Все реже я встречаю книги, где было бы описано взаимодействие приложений посредством DDE. Многие и вовсе думают, что DDE уже давно умер. Но, нет, “жив курилка”. И, более того, это одно из самых быстрых решений по передаче данных в Excel. И, что самое интересное, Excel по-прежнему поддерживает DDE и все DDE-команды, которые стали доступны со времен версии 4.0. И по-прежнему будет несказанно рад тот счастливчик, который обнаружит в непроходимых джунглях Сети файл под названием “excel40macro.hlp”, ибо там он найдет все, что нужно для быстрой и качественной работы с Excel. Это вам не интерфейсы “пользовать”. Но, вернемся к исходному тексту.
В этой процедуре я, как было описано выше, прохожу по всей таблице, собирая в строковый буфер значения полей, разделенные символом табуляции. Записи же разделяются символом перевода строки. Затем я получаю интерфейс на область, куда необходимо поместить данные из таблицы. Это первая часть кода. Далее - самое интересное.
Переменная xlDDE используется для доступа к Excel посредством DDE. Если опустить теорию, напрямую обратившись к практике, то можно увидеть следующий алгоритм ее использования. Во-первых, создается экземпляр класса TDDEClientConv. Во-вторых, вызовом SetLink происходит соединение через DDE с запущенным Excel. SetLink возвращает true, если это соединение успешно. А далее происходит вызов метода PokeData, одним из параметров которого является строковый буфер Buff. Второй параметр - это адрес области в формате R1C1. Вот и все. Думаю, это работает и у вас. Скорость сравнима с CSV через буфер обмена. Плюс, здесь буфер обмена мы совсем не используем. Но!
Попробуйте несколько раз подряд быстро нажать кнопку “Send data” с этим вариантом передачи данных. У меня Excel просто виснет. Точнее, он что-то делает, загружая на все сто процессор. Благо, Windows NT безболезненно позволяет снять задачу. Тем, у кого Win9x, повезло меньше и, видимо, им придется перезагрузиться. Почему это происходит? Меня смутила вот эта строка в реализации TDDEClientConv:
hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), FConv, hszItem,
FDdeFmt, XTYP_POKE, TIMEOUT_ASYNC, nil);
Точнее параметр TIMEOUT_ASYNC, позволяющий передавать данные асинхронно. Вот и сыплется Excel, не выдерживая реализации DDE-клиента от Borland/Inprise. Впрочем, Borland тоже не причем. Для себя я создал потомка класса TDDEClientConv, добавив ему новый метод xlPokeData, в котором просто заменил эту строку на:
Const xddeTransactionTimeOut = 100000;
…
hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), Conv, hszItem,
CF_XLTABLE, XTYP_POKE, xddeTransactionTimeOut, nil);
…
И все в порядке - работает.
Я не стану описывать подробности взаимодействия процессов через DDE по, думаю, понятным вам причинам. Все это давно описано в классике жанра - документации по Delphi. Тем не менее, по-прежнему остается проблема со строкой “0001″. И останется она при этом варианте нерешенной, так как здесь используются строковые представления всех значений полей. И где же выход, спросите вы? Выход прост.
Из всего вышесказанного я бы хотел сделать один вывод. Если есть желание получить максимальное быстродействие, то необходимо использовать DDE. Впрочем, для меня это уже аксиома. Единственное, чего здесь не достает, так это такого формата, чтобы…
Чтобы в нем использовались native представления данных, где число было бы не строкой, а привычным набором из четырех (восьми) байт. Ведь, напомню, целые и вещественные числа и даты - это, в конечном счете, вещественное число в Excel. Для себя я решение нашел - Fast Table Format. Но это уже не из этой статьи…
Демо-Проект - SampleExcel3 (в архиве с данной статьёй).
Февраль
11,
2008
— Рубрика: Delphi
Метки: DDE, SampleExcel, Table Format
