Delphi excel не найдено

Содержание

  1. Delphi excel не найдено
  2. Работа с Excel в Delphi. Основы основ.
  3. Введение
  4. 1. Как проверить установлен ли Excel на компьютере пользователя?
  5. Delphi. Записки на полях.
  6. Основы работы с Excel в Delphi.
  7. 1. Как проверить установлен ли Excel на компьютере пользователя?
  8. 2. Как определить запущен ли Excel?
  9. 3. Как запустить Excel?
  10. 4. Создаем пустую рабочую книгу Excel
  11. 5. Сохраняем рабочую книгу и закрываем Excel
  12. «Delphi 4: Автоматизация приложений MS® Office® для эффективного анализа результатов»
  13. Глава 1. Работа с MS Excel.
  14. Статьи, похожие по тематике
  15. Комментарии читателей к данной статье

Delphi excel не найдено

Пожалуйста, выделяйте текст программы тегом [сode=pas] . [/сode] . Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

Соблюдайте общие правила форума

Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как «свернуть» программу в трей.
3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как запустить программу/файл? (и дождаться ее завершения)
5. Как перехватить API-функции, поставить hook? (перехват сообщений от мыши, клавиатуры — внедрение в удаленное адресное прстранство)
. (продолжение следует) .
Внимание:
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка — 60 дней. Последующие попытки — бан.
Мат в разделе — бан на три месяца.
Полезные ссылки:
MSDN Library FAQ раздела Поиск по разделу Как правильно задавать вопросы

Выразить свое отношение к модераторам раздела можно здесь: Rouse_, Krid

Источник

Работа с Excel в Delphi. Основы основ.

Видимо любители экономить килобайты оперативной памяти могут меня закидать помидорами или ещё чем по-хуже, но все-таки я скажу, что интеграция приложений (мегабайты оперативы) — это большой плюс нынешней разработки приложений.
Как ни крути, а время DOS и килобайтов оперативной памяти прошло. Вряд ли кто-то всерьез сейчас задумывается над тем куда это с винчестера пропал мегабайт? Да и использование в своих приложениях функциональности программ, которых ты не писал, но которые выполняют что-то лучше — это всё-таки больший прогресс, нежели корпеть год-два над программой, а потом узнать, что время-то прошло.

Введение

Итак, цель сегодняшней статьи — поделиться с Вами опытом работы с Microsoft Excel в приложениях, написанных на Delphi.
Вспомнился сейчас один случай. Когда я только начинал работать по своей специальности, пригласили меня написать программу-расчётник для экологов нашего нефтезавода. В принципе ничего серьёзного — программа считает выброс от нагревательной печи и выдает табличку результатов, которую необходимо распечатать и уложить в толстую папку с отчётами. Естественно, что в области разработки подобных приложения я далеко не пионер, поэтому дали взглянуть на аналог будущей программы, который работал ещё под DOS и печатались отчёты на дико скрипящем матричном принтере с 12-ю иголками. Ну посмотрел, элементарная таблица, расчёт немного запутан, но жить можно — начал по-тихоньку писать. И попалась мне тогда на глаза статейка про работу с Excel в Delphi. Вот я и решил попробовать выдавать отчёт не только на форму приложения, а ещё и скидывать весь ход расчёта с формулами и прочим делом в Excel…Надо сказать более сильно детской радости начальника отдела я не видел до сих пор :). Люди, всю жизнь проработавшие в DOS увидели как тот же самый расчёт может выглядеть в современных условиях. Вот теперь при определении технических заданий на каждую новую программу, обязательно присутствует пункт, гласящий, что программа должна передавать данные либо в MS Word либо в MS Excel.Соответственно и цена на разработку возрастает, иногда значительно.

Отсюда можно сделать простой и однозначный вывод — заказчики готовы пожертвовать лишними деньгами только для того, чтобы всё в программе было красиво. Excel может дать вашему приложению ту самую красоту и удобство.

Ну, а для того, чтобы каждый раз не утруждать себя выполнением однотипных операций, я разработал небольшой модуль для работы с Excel. Этот же модуль я в настоящее время дорабатываю под ещё одну задачу, но об этом после. Сегодня основы основ работы с Excel в Delphi.

1. Как проверить установлен ли Excel на компьютере пользователя?

Создаем новый модуль (unit) и подключаем в uses следующие модули:

Источник

Delphi. Записки на полях.

Основы работы с Excel в Delphi.

Видимо любители экономить килобайты оперативной памяти могут меня закидать помидорами или ещё чем по-хуже, но все-таки я скажу, что интеграция приложений (мегабайты оперативы) — это большой плюс нынешней разработки приложений.
Как ни крути, а время DOS и килобайтов оперативной памяти прошло. Врядли кто-то всерьез сейчас задумывается над тем куда это с винчестера пропал мегабайт? Да и использование в своих приложениях функциональности программ, которых ты не писал, но которые выполняют что-то лучше — это всё-таки больший прогресс, нежели корпеть год-два над программой, а потом узнать, что время-то прошло.

Итак, цель сегодняшней статьи — поделиться с Вами опытом работы с Microsoft Excel в приложениях, написанных на Delphi.
Вспомнился сейчас один случай. Когда я только начинал работать по своей специальности, пригласили меня написать программу-расчётник для экологов нашего нефтезавода. В принципе ничего серьёзного — программа считает выброс от нагревательной печи и выдает табличку результатов, которую необходимо распечатать и уложить в толстую папку с отчётами. Естественно, что в области разработки подобных приложения я далеко не пионер, поэтому дали взглянуть на аналог будущей программы, который работал ещё под DOS и печатались отчёты на дико скрипящем матричном принтере с 12-ю иголками. Ну посмотрел, элементарная таблица, расчёт немного запутан, но жить можно — начал по-тихоньку писать. И попалась мне тогда на глаза статейка про работу с Excel в Delphi. Вот я и решил попробовать выдавать отчёт не только на форму приложения, а ещё и скидывать весь ход расчёта с формулами и прочим делом в Excel…Надо сказать более сильно детской радости начальника отдела я не видел до сих пор :). Люди, всю жизнь проработавшие в DOS увидели как тот же самый расчёт может выглядеть в современных условиях. Вот теперь при определении технических заданий на каждую новую программу, обязательно присутствует пункт, гласящий, что программа должна передавать данные либо в MS Word либо в MS Excel.Соответственно и цена на разработку возрастает, иногда значительно.

Отсюда можно сделать простой и однозначный вывод — заказчики готовы пожертвовать лишними деньгами только для того, чтобы всё в программе было красиво. Excel может дать вашему приложению ту самую красоту и удобство.

Ну, а для того, чтобы каждый раз не утруждать себя выполнением однотипных операций, я разработал небольшой модуль для работы с Excel. Этот же модуль я в настоящее время дорабатываю под ещё одну задачу, но об этом после. Сегодня основы основ работы с Excel в Delphi.

1. Как проверить установлен ли Excel на компьютере пользователя?

Создаем новый модуль (unit) и подключаем в uses следующие модули:

теперь объявляем глобальную переменную:

Одну константу (для удобства):

И пишем простенькую функцию:

Если функция CLSIDFromProgID находит CLSID OLE-объекта, то, соответственно — Excel установлен.

Но проверка на наличие установленного Excel — это только часть необходимых операций перед началом работы. Другой немаловажной проверкой является проверка на наличие уже запущенного экземпляра Excel. Если этого не делать, то может случиться такая нехорошая ситуация, когда в системе будет зарегистрировано …дцать процессов Excel и все оперативная память волшебным образом утекет «в трубу» — за такое могут и по ушам надавать. Но мы-то свои уши бережем. Пишем вторую функцию проверки.

2. Как определить запущен ли Excel?

Думаю тут лишних объяснений не потребуется? Всё предельно просто — если есть активный процесс Excel, то му просто получаем на него ссылку и можем использовать Excel для своих корыстных целей. Главное — не забыть проверить — может кто-то этот самый Excel забыл закрыть, но это другой момент. Будем считать, что Excel в нашем полном распоряжении.

3. Как запустить Excel?

Одно дело, когда мы получаем в распоряжение уже запущенный Excel, другое — когда Excel требуется запустить из Delphi. Напишем функцию запуска Excel:

Здесь мы в начале проверяем, установлен ли Excel в принципе и, если он все же установлен, запускам. При этом мы можем сразу показать окно Excel пользователю — для этого необходимо выставить параметр Visible в значение True.

Также рекомендую всегда отключать системные сообщения Excel, иначе, когда программа начнет говорить голосом Excel — пользователь может занервничать.

Переходим к следующему этапу работы — созданию рабочей книги.

4. Создаем пустую рабочую книгу Excel

Для создания пустой рабочей книги я обычно использую вот такую функцию:

Т.е. сразу проверяю запущен ли Excel и, если он не запущен, то либо запускаю и добавляю книгу, либо просто выхожу — всё зависит от ситуации.

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

Функция возвращает список TStringList всех рабочих книг Excel открытых в данный момент. Обратите внимание, что в отличие от Delphi Excel присваивает первой книге индекс 1 , а не 0 как это обычно делается в Delphi при работе, например, с теми же индексами в ComboBox’ах.

Ну, и наконец, после того, как поработали с книгами — их требуется закрыть. Точнее сохранить, а потом уж закрыть.

5. Сохраняем рабочую книгу и закрываем Excel

Для того, чтобы сохранить рабочую книгу, я использовал такую функцию:

Если у Вас открыто 10 книг — просто вызываете функцию 10 раз, меняя значение параметра WBIndex и имени файла и дело в шляпе.

А закрывается Excel вот так:

Вот набор тех основных функций с которых начинается вся интеграция Excel в приложения написанные на Delphi. В следующий раз займемся работой с конкретной книгой — научимся записывать и читать данные из книг.

Источник

Сегодня:
000

Последние:
— 09.08, 09:39 / #6696
— 29.03, 23:32 / #6682
— 30 апреля 2012

Сейчас онлайн:
На сайте — 4
На IRC-канале — 2

«Delphi 4: Автоматизация приложений MS® Office® для эффективного анализа результатов»

    Содержание:
  • Глава 1: Работа с MS Excel.
    • Часть 1: Создание, отображение и удаление экземпляра Excel.
    • Часть 2: Лучшее решение — шаблоны.
    • Часть 3: Создание или открытие книги.
    • Часть 4: Работа с листами и ячейками.
    • Часть 5: Передача данных разного типа.
    • Часть 6: Передача данных используя буфер обмена и DDE.
    • Часть 7: Пример обмена данными с Excel используя VCL и OLE.
  • Глава 2: Работа с MS Word.
    • Часть 1: Управление Word-ом через OLE.
    • Часть 2: Подсчет статистики обычного текста, сносок и колонтитулов в документах.
    • Часть 3: Открытие документа используя VCL.
    • Часть 4: Работа с таблицами.
    • Часть 5: Работа с текстом, рисунками и списками.
  • Автор статей: Евгений Старостин.

    Глава 1. Работа с MS Excel.

    Часть 1. Создание, отображение и удаление экземпляра Excel.

    Собственно, цель этой статьи мне понятна — поделиться своим опытом с народом. Делюсь.

    Итак, зачем нам, лучшим в мире программистам, нужен Excel, порождение «злого» гения Microsoft? Конечно, часто это лишнее — «юзать» Excel для отчетов. Напечатать «платежку» можно и в QReport-е. Но.

    Есть заказчики, готовые отдать «кучищи» денег за то, что они будут знать все и всегда о своем предприятии. Да еще, чтоб это было красиво и со вкусом.

    Приезжает один из моих заказчиков (немец — они повсюду! курорты Испании просто куплены ими — это знаю наверняка) на свое местное предприятие и начинает задавать интересные вопросы. Как трудились за время его отсутствия, сколько продукции выпустили, кому сколько отгрузили, в разных валютах, итого в USD и пр.? А я ему в ответ открываю отчет, неслабый такой, — сводная таблица по движению готовой продукции (посвященные знают, что это 40-ой счет в бухгалтерии). А в ней одних PageField-ов десяток. И на каждый его вопрос я начинаю отвечать не напрягаясь, потихоньку перетаскивая поля таблицы туда-сюда, фильтрую кое-что, строю диаграммы. Что, вы думаете, было потом? Он, как маленький ребенок, сидел за этой сводной таблицей несколько часов, все восхищался. И правильно, наши программисты круче ихних! Заодно и мы спокойно поработали (ему занятие нашлось). О деньгах тут вообще не говорим.

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

    Я бы привел еще несколько примеров, но, думаю, читатели уже поняли меня. Excel — вещь практически незаменимая во всяческих анализах (не путать с поликлиникой). А для тех, кто не понял, я еще напишу. Отдельно.

    Так как же с ним работать ?

    А просто. Создал «Excel.Application», использовал его по назначению, «убил» и готово. Вот именно об этом я и попытаюсь написать здесь.

    Важно! Параллельно с написанием статьи создавался демо-проект (точнее два — для Delphi 4 и 5), где вы сможете найти весь код примеров статьи. Проект для Delphi 4.0 использует импортированную Type Library из Excel 97. Здесь я использую ранее связывание, ибо CreateOLEObject отлично описал мой любимый классик в «Delphi 4 Unleashed» (мне ли с ним тягаться?). Кроме того, обращайтесь к комментариям в исходных текстах этого проекта. Местами там написано намного понятней, нежели здесь. Delphi 5 содержит более удобный механизм импорта библиотек типов с поддержкой событий и прекрасной генерацией ко-классов. Специально для счастливых обладателей Delphi 5 (я тоже им являюсь) я создал проект, но уже применительно к TexcelApplication (правда ли, что импортированный MS Office есть только в версии Enterprise?). Примеры кода я буду приводить сначала для Delphi 4, потом для Delphi 5. Заранее приношу прощения за дублирование информации в комментариях и в статье — писал сразу везде.

    И еще. Эффективная работа с Excel-ом из Delphi-приложений немыслима без знания одной важной вещи. И имя ей — интерфейс. Мне, конечно, хотелось бы написать о принципах работы с интерфейсами здесь, в этой статье. Более того, я обещал сделать это самой Королеве. Но.

    Мне ли (совсем еще не профессионалу — и это так!) пытаться сделать это лучше, чем классики этой области. Я честно признаюсь, что не смогу этого сделать быстро (в небольшом объеме) и качественно. Поэтому всякого, не знакомого еще с этой областью программирования, я с глубочайшими извинениями отсылаю к книге Чеппела «OLE Inside».

    Достойную помощь (уже применительно к Delphi) может вам оказать «Delphi 4 Unleashed» Чарльза Калверта.

    Создание экземпляра Excel.Application.

    Модуль импортированной Excel TLB (неважно, для D4 или D5) содержит описания всех интерфейсов, которые правильные программисты из Microsoft решили выставить наружу. Там есть все необходимое: типы, константы и интерфейсы. Этого вполне достаточно для работы с Excel-ом из Delphi-приложения (во написал! а что еще нужно-то?). Я создаю Excel для последующего его использования с помощью такого кода:

    procedure TForm1.CreateExcel(NewInstance: boolean);
    var IU: IUnknown;
    isCreate: boolean;
    begin
    // FIXLSApp — private-поле у формы
    // у меня в привычке добавлять букву I для всех интерфейсов
    // понятно почему FI. ?
    if not Assigned(FIXLSApp) then begin // а зачем создавать, если уже есть?
    isCreate := NewInstance or
    (not SUCCEEDED( GetActiveObject(CLASS_Application_, nil, IU) ) );
    if isCreate then
    FIXLSApp := CreateComObject(CLASS_Application_) as _Application
    else
    FIXLSApp := IU as _Application;
    end;
    end;

    Этот достаточно простой код вы найдете практически во всех книгах, посвященных работе с интерфейсами. Как и везде, я напишу, что в результате выполнения этого кода создастся объект COM с CLSID-ом «<00024500-0000-0000-c000-000000000046>» (читайте и перечитывайте Калверта, это не только укрепляет сон!).

    procedure TForm1.CreateExcel(NewInstance: boolean);
    begin
    if not Assigned(IXLSApp) then begin
    FIXLSApp := TExcelApplication.Create(Self);
    if NewInstance then FIXLSApp.ConnectKind := ckNewInstance;
    FIXLSApp.Connect;
    end;
    end;

    В отличие от предыдущих версий, Delphi 5.0 предоставляет более удобный сервис при импорте библиотек типов. Большой шаг вперед — появление класса ToleServer с поддержкой событий. Теперь работа с существующими и создание новых OLE-серверов стала намного удобней. Как видите, не приходится обращаться к низкоуровневым функциям. Впрочем, в Delphi 4.0 тоже существовал этот класс, только не от Borland. Отличная библиотека была создана Бином Ли (Binh Ly) в COM Nodes — это Threading COM Library. С легкой руки Алексея Вуколова (специальное спасибо!) я использовал ее для построения масштабируемых COM-серверов в сервисах WinNT.

    Обращу ваше внимание только на параметр NewInstance. Он позволяет создать новый процесс. Я часто задаю себе вопрос — «А нужен ли NewInstance?». Одна копия процесса, все ж, требует меньше памяти. Но еще чаще я думаю — «Боже, как хорошо я сделал, когда создал новый процесс!». Почему? Если вы не хотите потерять уже открытые, но еще не сохраненные книги, экспериментируя даже с моими примерами, создавайте новый процесс. Печальный опыт научил меня использовать GetActiveObject только в случае полной уверенности в коде, который будет выполняться после. Поэтому, мой вам совет, тестируйте свои приложения только с NewInstance. Или закрывайте важные книги пред этим. Excel — хитрая программа, бывает, улетает в неизвестность, ни слова не сказав. Это не вина Microsoft. Это неудачное расположение звезд.

    Как показать Excel, если он, разумеется, создан ?

    Вот здесь начинаются хитрости. Любой, читавший помощь по Excel VBA, скажет, что достаточно написать FIXLSApp.Visible := true. Не тут-то было. Я делаю так:

    procedure TForm1.ShowExcel;
    begin
    if Assigned(FIXLSApp) then begin // а если он не создан?
    FIXLSApp.Visible[0] := true;
    if FIXLSApp.WindowState[0] = TOLEEnum(xlMinimized) then
    FIXLSApp.WindowState[0] := TOLEEnum(xlNormal);
    FIXLSApp.ScreenUpdating[0] := true;
    end;
    end;

    Зачем здесь условие на минимайз и какой-то ScreenUpdating? Давайте попробуем закомментировать эти строки, остаиви только Visible, запустить проект, создать Excel (кнопка CreateExcel), показать его (кнопка ShowExcel), минимизировать, вернуться в приложение и сделать снова ShowExcel. Да-да, Visible = true переводит фокус в минимизированный Excel, не восстанавливая размеры окна. Это ситуация, с которой я борюсь условием на xlMinimized. Но ScreenUpdating зачем?

    Знающие люди говорят, что это свойство отвечает за перерисовку окон Excel. Это все равно, что DisableControls у TDataSet. Добавляет скорости, если в нем false. И это правда что, если выключить его во время длительных пересчетов, то быстрее пересчитается. Но мы, ведь, не выключали его. Зачем тогда эта строка?

    Делаем так: комметируем эту строку, запускаем демо, CreateExcel, ShowExcel, закрываем его (можно кнопкой с крестиком в правом верхнем углу окна, кому нравится — через меню «Файл/Выход»). Знающие люди скажут, что Excel на самом деле не закрыт. Интерфейс мы не освободили, поэтому в TaskManager мы его и увидим. Итак, Excel по-прежнему у нас в руках. Мы имеем право сделать ему снова Show.

    После такого действия у меня возникает ощущение, что я переплатил за свою видеокарту. Фокус в Excel-е, но я по-прежнему наблюдаю форму демо-проекта. Видимо, программисты из MS не рассчитывали на то, что кто-то закроет Excel, вызванный через создание Excel.Application, а потом захочет увидеть его снова. Но я-то захотел?!

    Свойства Visible, WindowState и ScreenUpdating вызываются с каким-то непонятным индексом массива — 0. В модуле Excel TLB во многих свойствах и методах вы можете встретить параметр или индекс lcid. Не помню, у кого я это прочитал (Калверт или Канту), но с тех пор я туда передаю всегда 0. И все работает. LCID — это что-то насчет локализации. В MSDN написано «Indicates that the parameter is a locale ID (LCID)».

    Спрячем Excel от посторонних глаз!

    На свой процесс я всегда создаю один экземпляр Excel.Application. Уже пару лет все отчеты у меня — это отчеты Excel. Я написал несколько классов, которые мне очень помогают в этом. Сегодня у меня целая «отчетная» подсистема, зашитая в класс и обслуживающая непомерно большие запросы моих пользователей. В промежутках между работой с отчетами нет необходимости «мозолить глаза» лишним окном в TaskBar-е. Вот и прячу я этот Excel. Это очень просто и комментариев, думаю, не требует:

    Delphi 4.0 / 5.0
    procedure TForm1.HideExcel;
    begin
    if Assigned(FIXLSApp) then begin
    FIXLSApp.Visible[0] := false;
    end;
    end;

    Закроем Excel корректно!

    Собственно говоря, при закрытии приложения Excel сам будет закрыт, если вы там не устели чего-нибудь отредактировать. И это правильно. Программисты Borland (Inprise до сих пор мне режет слух, да и некоторым в Inprise, судя по всему, тоже) позаботились об этом. Но я еще с Delphi 3 заимел дурную привычку освобождать все самостоятельно. Освобождать обычным присваиванием в nil (это касается проекта для D4). Труда это не составляет, да и проверка на Assigned удобна. Поэтому, и еще из кое-каких соображений, я делаю так:

    procedure TForm1.ReleaseExcel;
    begin
    if Assigned(FIXLSApp) then begin
    if (FIXLSApp.Workbooks.Count > 0) and (not FIXLSApp.Visible[0]) then begin
    FIXLSApp.WindowState[0] := TOLEEnum(xlMinimized);
    FIXLSApp.Visible[0] := true;
    Application.BringToFront;
    end;
    end;
    FIXLSApp := nil;
    end;

    Ну вот, написал только про nil, а кода — на полстраницы. Опишу ситуацию.

    Вы не запускали новый процесс, вы «законнектились» к уже существовавшему. В нем была открыта книга. Попробуйте: CreateExcel, ShowExcel, HideExcel (имеем право), ReleaseExcel. Если оставить только присваивание в nil, то существовавший процесс не будет выгружен (он же существовал до запуска нашего демо), но будет спрятан от пользователя с его открытой книгой.

    procedure TForm1.ReleaseExcel;
    begin
    if Assigned(IXLSApp) then begin
    if (IXLSApp.Workbooks.Count > 0) and (not IXLSApp.Visible[0]) then begin
    IXLSApp.WindowState[0] := TOLEEnum(xlMinimized);
    IXLSApp.Visible[0] := true;
    if not(csDestroying in ComponentState) then Self.SetFocus;
    Application.BringToFront;
    end;
    end;
    FreeAndNil(FIXLSApp);
    end;

    Практически тот же код. Только в D5 вы работаете уже не с интерфейсом напрямую, а с экземпляром класса TexcelApplcation. Если посмотреть его предков, то можно увидеть, что это настоящий класс, освободить который просто необходимо. Поэтому вместо присваивания в nil там написано FreeAndNil (помните такую процедуру?).

    Часть 2: Лучшее решение — шаблоны.

    Excel, интегрированный с моими приложениями, хорош (для меня — программиста) только по одной причине. Я всегда создаю шаблоны и использую их потом при построении отчетов. Шаблоны позволяют мне избежать ручного (в исходном тексте) форматирования. В общем случае, алгоритм выглядит просто: по шаблону создается книга, каким-то образом помеченные области заполняются данными и. (а дальше все уже готово). Как я создаю книгу по шаблону:

    function TForm1.AddWorkbook(const WorkbookName: string): Excel8TLB._Workbook;
    begin
    Result := nil;
    if Assigned(FIXLSApp) and (trim(WorkbookName) <> ») then begin
    Result := FIXLSApp.Workbooks.Add(WorkbookName, 0);
    end;
    end;

    В этом коде нет ничего сложного. В принципе при работе с Excel я мало находил мест, где что-либо сделать было бы сложно. Чаще достаточно прочитать справку по VBA или записать макрос (благо, Microsoft встроила в Excel хороший пишущий player). После выполнения этого метода будет добавлена книга, близнец шаблона, с именем шаблона и порядковым номером (как «Книга1.xls» или «Книга228.xls»). Правда здесь есть одна тонкость. Эти «циферки» в имя книги Excel добавляет после поиска книг с таким же названием в каталоге по умолчанию. Я несколько раз наступал на грабли (больно!), когда пытался сохранять книги в другом каталоге и создавать новую — по этому же шаблону. К сожалению, не может эта «злобная» программа держать открытыми несколько книг с одинаковыми названиями, несмотря на то, что они лежат в разных каталогах.

    Как я помечаю области, в которые необходимо разместить данные? В Excel существует возможность объединить ячейки в группу и поименовать эту группу. В терминах Microsoft это объект Range (область). Для своего проекта я создал тестовую книгу «Test.xls», в которой на листе «Лист1» разместил область «TestRange» (см. рисунок). Более того, для ячеек этой области я указал форматы вывода (Field4 — дата, Field3 — красный цвет шрифта). Я надеюсь, что после переноса тестовых данных форматы сохранятся.

    Что есть шаблон без данных в нем?

    Существует масса способов передать данные в Excel, начиная с DDE и заканчивая обычным присваиванием (типа Cell.Value := NewValue ). Конечно, максимальную скорость передачи данных можно получить, только используя DDE. Но я отказался от этого пути из-за некоторых ограничений и давно смущающего меня флажка в настройках Excel («Игнорировать DDE-запросы»). Поэтому здесь я опишу менее эффективный, но работоспособный, путь решения этой проблемы. Итак, после нажатия кнопки CreateExcel имеем открытый шаблон с листом «Лист1» и областью с именем «TestRange». Для чистоты эксперимента (скорей из лени, великая вещь — собственная лень) я описал константный массив с тестовыми данными — TestDataArray. Именно эти данные я и передаю в ячейки области:

    procedure TForm1.btnDataToBookClick(Sender: TObject);
    var LaunchDir: string;
    IWorkbook: Excel8TLB._Workbook;
    ISheet: Excel8TLB._Worksheet;
    IRange: Excel8TLB.Range;
    NewValueArray, V: OLEVariant;
    i: integer;
    begin
    if Assigned(IXLSApp) then begin
    LaunchDir := ExtractFilePath( ParamStr(0) );
    IWorkbook := AddWorkbook(LaunchDir + ‘Test.xls’);
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    IRange := ISheet.Range[‘TestRange’, EmptyParam];
    NewValueArray := VarArrayCreate([0, 20, 1, 4], varVariant);
    for i := 0 to 20 do begin
    NewValueArray[i, 1] := TestDataArray[i].V1;
    NewValueArray[i, 2] := TestDataArray[i].V2;
    NewValueArray[i, 3] := TestDataArray[i].V3;
    NewValueArray[i, 4] := date + i;
    end;
    IRange.Value := NewValueArray;
    finally
    IRange := nil;
    ISheet := nil;
    IWorkbook := nil;
    end;
    end;
    end;

    procedure TForm1.btnDataClick(Sender: TObject);
    type
    var LaunchDir: string;
    IWorkbook: Excel97.ExcelWorkbook;
    ISheet: Excel97.ExcelWorksheet;
    IRange: Excel97.Range;
    NewValueArray, V: OLEVariant;
    i: integer;
    begin
    if Assigned(IXLSApp) then begin
    LaunchDir := ExtractFilePath( ParamStr(0) );
    IWorkbook := AddWorkbook(LaunchDir + ‘Test.xls’);
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel97.ExcelWorksheet;
    IRange := ISheet.Range[‘TestRange’, EmptyParam];
    NewValueArray := VarArrayCreate([0, 20, 1, 4], varVariant);
    for i := 0 to 20 do begin
    NewValueArray[i, 1] := TestDataArray[i].V1;
    NewValueArray[i, 2] := TestDataArray[i].V2;
    NewValueArray[i, 3] := TestDataArray[i].V3;
    NewValueArray[i, 4] := date + i;
    end;
    IRange.Value := NewValueArray;
    finally
    IRange := nil;
    ISheet := nil;
    IWorkbook := nil;
    end;
    end;
    end;

    Я знаю, что многие профессионалы (к коим, к сожалению, я не принадлежу) взвоют от негодования при виде такого кода. Их можно понять, они знают намного более эффективные решения. Именно об этих решениях я и собираюсь написать в следующий раз. А вы пока не забудьте почитать комментарии в проекте-примере.

    Часть 3. Создание или открытие книги.

    В качестве примера я беру проект из предыдущей моей статьи и стану его понемногу расширять, отвечая на вопросы, появившиеся у специалистов разного профиля и кругозора. Эти вопросы получены мною из двух «источников»: как реакция на мою статью и, извините, из переписки по XL Report Support. Эти две вещи уж очень сильно пересекаются, поэтому я и обращаюсь к обоим источникам моего вдохновения. Я не буду последователен в своих рассуждениях, местами буду писать подробно, местами — кратко. Попросту, я опишу некоторые часто встречающиеся проблемы и решения этих проблем.

    И еще! Я решил совсем опустить в своем пространном (как обычно) повествовании тонкости работы с Excel в Delphi 5.0, так как считаю, что работа с импортированной библиотекой типов принципиально одинакова и в версии 4, и в версии 5. Различен, разве что, только уровень импорта этой самой библиотеки. К тому же, я уже полностью «переехал» на Excel 2000, поэтому тестирую весь код, который приведен здесь, именно в нем. Итак, поехали.

    Создание или открытие книги.

    Повторюсь, не смотря на то, что я уже писал об этом в предыдущей статье. В главной форме проекта-примера я объявил свойство IWorkbook. Оно будет содержать интерфейс книги, которую мы будем создавать и использовать. Естественно, в обработчике FormDestroy я его освобождаю.

    property IWorkbook: Excel8TLB._Workbook read FIWorkbook;

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

    if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
    FIWorkbook := IXLSApp.Workbooks.Add(EmptyParam, 0);

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

    Коллекция Workbooks содержит все открытые книги и предоставляет возможность кое-как управлять всем этим.

    Боже, как убоги коллекции от Microsoft, и особенно поиск в них! Я отклонюсь, но это надо видеть. Вот пример поиска книги с заданным именем, приведенный как совет в MSDN Office Developer.

    Public Function SheetExists(strSearchFor As String) As Boolean
    SheetExists = False
    For Each sht In ThisWorkbook.Worksheets
    If sht.Name = strSearchFor Then
    SheetExists = True
    End If
    Next sht
    End Function

    Это вам не IndexOf писать. Сами ищите! А я так иделаю. Но, далее.

    Метод Add этой коллекции (читай, метод интерфейса) позволяет добавить книгу к этой коллекции, пустую либо по шаблону. Первый параметр этого метода, Template (из справки по Excel VBA), может принимать имя файла с путем.

    Поэтому, выполнив код

    if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
    FIWorkbook := IXLSApp.Workbooks.Add(ExtractFilePath(ParamStr(0)) + ‘Test.xls’, 0);

    вы получите книгу, идентичную файлу «Test.xls» с именем Test1.xls. Именно этим способом я создаю все свои отчеты, так как создаю их по заранее разработанным шаблонам. Естественно, что это шаблоны XL Report.

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

    if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
    FIWorkbook := IXLSApp.Workbooks.Open(ExtractFilePath(ParamStr(0)) + «Test.xls’, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, false, 0);

    Понимаю, что в душе нормального программиста такой код вызовет отвращение. Как-то я даже получил гневное письмо о собственной ненормальности из-за того, что использую ранее связывание и кучу EmptyParam. Впрочем, я не сильно агрессивный человек (правда, только в переписке), и отвечать не стал. В конечном итоге, раннее связывание дает мне немного преимуществ, но я за него. Я не могу помнить все методы и их параметры из Excel Type Library, поэтому получаю их (только при раннем связывании, естественно) из подсказок редактора Delphi — продуманная вещь этот редактор. А чтобы не мучаться с написанием такого количества EmptyParam, можно написать и так:

    if Assigned(IXLSApp) and (not Assigned(IWorkbook)) then
    IDispatch(FIWorkbook) := OLEVariant(IXLSApp.Workbooks).Open(
    FileName := ExtractFilePath(ParamStr(0)) + ‘Test.xls’);

    Но, мы отклонились. Что же стоит за таким количеством параметров по умолчанию в методе Open? Да, много чего. Из этого «громадья» я использую лишь несколько вещей. Их я и опишу, а заинтересовавшихся остальными отсылаю к справке по Excel VBA. Вот объявление этого метода в импортированной библиотеке типов:

    function Open(const Filename: WideString; UpdateLinks: OleVariant; ReadOnly: OleVariant;
    Format: OleVariant; Password: OleVariant; WriteResPassword: OleVariant;
    IgnoreReadOnlyRecommended: OleVariant; Origin: OleVariant;
    Delimiter: OleVariant; Editable: OleVariant; Notify: OleVariant;
    Converter: OleVariant; AddToMru: OleVariant; lcid: Integer): Workbook; safecall;

    В FileName необходимо передать имя открываемого файла, желательно указав путь его нахождения. Иначе, этот файл Excel будет искать в каталоге по умолчанию. Чтобы файл был запомнен в списке последних открытых файлов, в AddToMru можно передать true. Иногда я знаю, что файл рекомендован только для чтения (не путать с «парольной» защитой книги). Тогда при открытии выдается соответствующее сообщение. Чтобы игнорировать его, можно передать в IgnoreReadOnlyRecommended true. Вот, пожалуй, и все мои скудные знания об этом методе. Впрочем, с помощью его мне приходилось открывать и файлы текстовых форматов с разделителями. Но тогда я обращался к чудесному «пишущему» плейеру VBA и записывал с его помощью макросы, затем правил их по необходимости и все отлично получалось. Этим же способом разрешать «всяческие» тонкие вопросы рекомендую и вам.

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

    procedure TForm1.btnCreateBookClick(Sender: TObject);
    var FullFileName: string;
    begin
    FullFileName := ExtractFilePath(ParamStr(0)) + ‘Test.xls’;
    if Assigned(IXLSApp) and (not Assigned(IWorkbook)) then
    try
    case rgWhatCreate.ItemIndex of
    // По шаблону
    0: FIWorkbook := IXLSApp.Workbooks.Add(FullFileName, 0);
    // Просто откроем
    1: FIWorkbook := IXLSApp.Workbooks.Open(FullFileName,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
    EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, false, 0);
    // Пустая книга
    2: FIWorkbook := IXLSApp.Workbooks.Add(EmptyParam, 0);
    end;
    except
    raise Exception.Create(‘Не могу создать книгу!’);
    end;
    end;

    Далее во всех примерах я подразумеваю, что вы всякий раз будете создавать новую книгу по шаблону с помощь кнопки «Create workbook». Книга-шаблон названа как прежде Test.xls и включена в проект. Все остальные примеры опираются именно на эту книгу и ее листы. В этой книге я подготовил кое-какие данные и поименованные области для последующих примеров работы с Excel. Для каждого примера кода я буду добавлять кнопку и, возможно, RadioGroup к ней с возможностью выбора варианта работы. Не судите меня строго за то, что главная и единственная форма проекта-примера получится громоздкой и некрасивой. Не это здесь главное. Итак, всегда создавайте по кнопке книгу. Далее нажимайте кнопку, на которую указывает конкретный пример кода, и наблюдайте. Буду рад, если кто-то из читателей создаст более приемлемый демонстрационный проект для этой статьи.

    Часть 4. Работа с листами и ячейками.

    Есть в VBA одна вещь, которая меня раздражает. Это ActiveSheet и ActiveWorkbook, а также возможность работы с Cells и Range без указания, к какому листу или книге они принадлежат. Одно время я боролся сам с собой, то применяя, то совсем отказываясь от подобных конструкций. Окончательно я отказался от этого лишь после обнаружения многочисленных ошибок в анализе «лога» моего Web-сервера, который я сделал на VBA. Благо, при работе в Delphi нет возможности написать Cells(x, y) = NewValue, подразумевая при этом какой-то неуловимый ActiveSheet. Поэтому прежде, чем работать с отдельными ячейками, я всегда получаю интерфейс на конкретный и вполне осязаемый лист книги. И делю это так:

    var ISheet: Excel8TLB._Worksheet;
    .
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;

    Коллекция Worksheet подобна всем остальным коллекциям из Excel TLB. В ней вы можете удалять листы, вставлять новые, изменять их порядок. Но я практически никогда не делаю этого, поэтому всех нетерпеливых снова отсылаю к справке по Excel VBA.

    Главную же мысль свою повторю еще раз. Всегда и везде рекомендую работать с ячейками и областями в контексте их листа, получив предварительно интерфейс на этот лист вышеописанным способом. От использования свойств ActiveSheet и ActiveWorkbook можно совсем отказаться, разве что за исключением каких-то особых случаев.

    Чтение данных из ячейки.

    Написав этот заголовок, я подумал о том, как часто я «беру» данные из книги. Это случается весьма редко, ибо Excel я использую как средство построения отчетов. То есть, намного чаще эти данные я туда передаю. Поэтому хотелось бы описать не столько чтение данных, сколько способы обращения к ячейкам. Я использую разные способы обращения к ячейкам от привычного в Excel Cells(x,y) до коллекции Names. Вот некоторые примеры:

    procedure TForm1.btnReadDataClick(Sender: TObject);
    var Value: OLEVariant;
    ISheet: Excel8TLB._Worksheet;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    try
    case rgWhatRead.ItemIndex of
    0: Value := ISheet.Cells.Item[2, 1].Value;
    1: Value := ISheet.Range[‘A2’, EmptyParam].Value;
    2: Value := ISheet.Range[‘TestCell’, EmptyParam].Value;
    3: Value := IWorkbook.Names.Item(‘TestCell’, EmptyParam,
    EmptyParam).RefersToRange.Value;
    end;
    ShowMessage(Value);
    finally
    ISheet := nil;
    end;
    except
    raise Exception.Create(‘Не могу прочитать данные!’);
    end;
    end;

    На главную форму проекта я добавил кнопку, по которой можно прочитать данные из ячейки «А2» открытой книги, и RadioGroup к ней, чтобы выбрать способ получения этих данных. Из приведенного кода видна одна из «гнуснейших» моих привычек — освобождать все полученные интерфейсы явно (ISheet := nil). Я не могу побороть ее уже долгое время, поэтому прошу прощения у мастеров программирования на Delphi за то, что эта строчка здесь абсолютно лишняя.

    Самый повторяющийся вопрос в моей почте, это вопрос о Cells. Почему-то многие уверены, что конструкция Cells[x, y].Value должна работать. В VBA это так. Но при раннем связывании это не соответствует истине. Свойство Cells объявлено у всех интерфейсов как

    property Cells: Range read Get_Cells;

    Отсюда видно, что это область (Range). И нет там никаких индексов, чтобы пробовать писать [x, y]. Один из корреспондентов мне написал, что он как-то обращался с этой проблемой к Наталье Елмановой, и она ему не помогла, написав, что «есть предположение, что кодогенератор Delphi их как-то не так «переваривает», генерируя XXX_TLB.pas, но полной уверенности нет». А дело в том, что «кодогенератор» правильно генерирует свойства _Defaul и Item (у многих интерфейсов в Excel Type Library есть такое свойство) у интерфейса Range. Вот только свойство _Default должно быть свойством по умолчанию. Поэтому стандартное объявление этих свойств

    property _Default[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 0;
    property Item[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 170;

    можно исправить на такой вариант

    property _Default[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 0; default;
    property Item[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 170;

    и смело писать Cells[x, y].Value.

    Понятное дело, что это нехорошо — редактировать код, полученный автоматически из умного «кодогенератора» Delphi. Но «Там», ведь, тоже люди работают и ошибаются они не реже наших. Кстати, в импортированной Excel Type Library (независимо от версии Delphi — 4 или 5) некоторые свойства, имеющие dispid 0, почему-то все-таки объявлены как default. Почему?!

    В приведенном выше примере кода я показал не только использование Cells. К ячейкам можно получить доступ и через свойство Range интерфейса Worksheet. Это свойство объявлено как

    property Range[Cell1: OleVariant; Cell2: OleVariant]: Range read Get_Range;

    В Cell1 / Cell2 можно передать ячейки (только в формате А1, RC вызовет исключение), описывающие границы области — левый верхний угол и правый нижний. Я же использовал только указание одной ячейки, мне необходимой. Где-то в Рунете я встретил предположение о том, что, если передать в оба параметра «A1», то в выбранный Range попадет вся колонка. Сначала я подумал, — «А почему не вся строка?!» Но, решил, все-таки проверить это предположение — в область попала одна ячейка.

    В Excel можно присваивать имена любым ячейкам и даже наборам ячеек. Это можно сделать, либо используя «комбо-бокс», который находится левее строки формул, либо пункт меню «ВставкаИмяПрисвоить». Ячейке «А2» я присвоил имя «TestCell» и, используя все то же свойство Range листа, получил значение ячейки по этому имени.

    И последний вариант, без которого я не смог бы обойтись при создании всех своих отчетов, это использование коллекции Names книги. Не смотря на некоторую неуклюжесть кода, этот способ я использую довольно часто. Почему? Потому, что очень часто использую именованные ячейки и области, разбросанные по разным листам и даже книгам. Но останавливаться на нем смысла не вижу, оставляя благодарному читателю возможность обратиться непосредственно к первоисточнику — справке по Excel VBA.

    Чтение данных из нескольких ячеек.

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

    procedure TForm1.btnReadArrayClick(Sender: TObject);
    var Values: OLEVariant;
    ISheet: Excel8TLB._Worksheet;
    IRange: Excel8TLB.Range;
    i, j: integer;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    try
    IRange := ISheet.Range[‘TestRange2’, EmptyParam];
    Values := VarArrayCreate([1, IRange.Rows.Count, 1, IRange.Columns.Count], varVariant);
    for i := 1 to IRange.Rows.Count do
    for j := 1 to IRange.Columns.Count do begin
    Values[i, j] := IRange.Item[i, j];
    ShowMessage( Values[i, j]);
    end;
    finally
    IRange := nil;
    ISheet := nil;
    end;
    except
    raise Exception.Create(‘Не могу прочитать данные в массив!’);
    end;
    end;

    Я создал на форме кнопку, по которой из заранее подготовленной области с именем «TestRange2» все значения ячеек будут получены в вариантный массив Values. Вызов ShowMessage добавлен сюда только для контроля над процессом. Как видно, получить значения ячеек области достаточно просто. Вы создаете вариантный массив с количеством строк и колонок, равными размерам области, а затем, проходя по очереди все ячейки области, запоминаете их значения в массиве. Но в этом коде есть одна проблема. Чтение из ячеек можно организовать еще проще. Вот так:

    procedure TForm1.btnReadArrayClick(Sender: TObject);
    var Values: OLEVariant;
    ISheet: Excel8TLB._Worksheet;
    IRange: Excel8TLB.Range;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    try
    IRange := ISheet.Range[‘TestRange2’, EmptyParam];
    Values := IRange.Value; //

    Дело в том, что строки Values := IRange.Value вполне достаточно. Свойство Value интерфейса Range в состоянии вернуть вариантный массив. Этот код, по моему мнению, более прост и производителен, особенно на больших объемах данных. Уберите отсюда циклы с ShowMessage и убедитесь в этом.

    А вот пример кода, который вернет в массиве значения всех ячеек из используемой области на листе. Проще сказать, вернет весь лист:

    var Values: OLEVariant;
    ISheet: Excel8TLB._Worksheet;
    IRange: Excel8TLB.Range;
    i, j: integer;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    try
    IRange := ISheet.UsedRange[0];
    Values := IRange.Value;
    finally
    IRange := nil;
    ISheet := nil;
    end;
    except
    raise Exception.Create(‘Не могу прочитать данные в массив!’);
    end;
    end;

    Здесь я использую свойство UsedRange листа. Это прямоугольная область, заключенная между «левой верхней непустой» и «правой нижней непустой» ячейками. (Кто-нибудь понял? Впрочем, в два часа ночи разве напишешь понятней!). Конечно, если в этой прямоугольной области будет много пустых ячеек, то массив получится с избыточными данными. Что бы убедиться в этом, попробуйте создать циклы с ShowMessage из предыдущего примера.

    В комментариях проекта-примера вы найдете еще несколько интересных конструкций, которые мне приходится использовать для получения массивов со значениями ячеек. В качестве параметра в UsedRange я передаю 0. Это lcid, описанный в предыдущей статье.

    Кстати, об lcid. В прошлый раз меня подвела зрительная память. И в самом деле, «любимый классик» пишет, что туда можно смело передавать 0. Но другой, не менее любимый классик с этим не согласен и рекомендует передавать туда результат функции GetUserDefaultLCID. Думаю, последнее более правильно. Однако В некоторых случаях, чаще в гремучей смеси Windows 2000 и Excel 2000, оба решения не проходили. Причем, выдавалось сообщение о попытке «использовать библиотеку старого формата:» и что-то еще. Так вот, вместо GetUserDefaultLCID я применяю теперь константу LOCALE_USER_DEFAULT. Более ничего объяснить не могу, так как до сих пор, проштудировав основательно MSDN, не разобрался, что же в таком случае хочет получить Microsoft в методы и свойства интерфейсов Excel, где одним из параметров требует lcid. Кто бы объяснил.

    Есть еще несколько способов чтения данных из книги, которые, впрочем, я не в силах описать здесь. Один их таких способов, это использование DDE, самый быстрый и экономичный (по ресурсам) способ, который известен еще со времен Windows 3.1.

    Поиск данных на листе.

    Предлагаю поискать все ячейки, содержащие строку (или подстроку) «Text», и изменить цвет фона этих ячеек. Для этого я использовал методы Find и FindNext. На форму была добавлена кнопка, в обработчике которой появился следующий код:

    procedure TForm1.btnFindClick(Sender: TObject);
    var ISheet: Excel8TLB._Worksheet;
    IFirst, IRange: Excel8TLB.Range;
    FirstAddress, CurrentAddress: string;
    UsedRange: OLEVariant;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    try
    UsedRange := ISheet.UsedRange[0];
    IDispatch(IFirst) := UsedRange.Find(What:=’Text’, LookIn := xlValues,
    SearchDirection := xlNext);
    if Assigned(IFirst) then begin
    IRange := IFirst;
    FirstAddress := IFirst.Address[EmptyParam, EmptyParam, xlA1, EmptyParam, EmptyParam];
    repeat
    IRange.Interior.ColorIndex := 37;
    IDispatch(IRange) := UsedRange.FindNext(After := IRange);
    CurrentAddress := IRange.Address[EmptyParam, EmptyParam, xlA1,
    EmptyParam, EmptyParam];
    until FirstAddress = CurrentAddress;
    end;
    finally
    IRange := nil;
    IFirst := nil;
    ShowExcel;
    end;
    except
    raise Exception.Create(‘Не могу чего-то сделать!’);
    end;
    end;

    Думаю, у каждого увидевшего этот код возникнет ощущение неудовлетворенности. Да, в выделенной красным строке никаким ранним связыванием и не пахнет. Более того, если вы попробуете вызвать метод Find с указанными параметрами, заменив остальные на EmptyParam, вы получите исключение. Есть места в Excel Type Library, работающие с ошибками. Я знаю достаточно этих мест. В таких случаях я использую приведенный здесь прием. Я проверяю работоспособность кода в редакторе VBA, а затем перехожу на позднее связывание. Так мне удалось обойти несколько серьезных, по моему мнению, ошибок в Excel TLB. Раннее связывание не должно быть догмой хотя бы из-за этого. Более того, перейдя полностью на ранее связывание, мы получим более компактный, а следовательно и читаемый код:

    procedure TForm1.btnFindClick(Sender: TObject);
    var ISheet: Excel8TLB._Worksheet;
    UsedRange, Range: OLEVariant;
    FirstAddress: string;
    begin
    if Assigned(IWorkbook) then
    try
    ISheet := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    UsedRange := ISheet.UsedRange[0];
    Range := UsedRange.Find(What:=’Text’, LookIn := xlValues, SearchDirection := xlNext);
    if not VarIsEmpty(Range) then begin
    FirstAddress := Range.Address;
    repeat
    Range.Interior.ColorIndex := 37;
    Range := UsedRange.FindNext(After := Range);
    until FirstAddress = Range.Address;
    ShowExcel;
    end;
    except
    raise Exception.Create(‘Не могу чего-то сделать!’);
    end;
    end;

    Перемещение данных между листами.

    Несколько раз меня спросили о том, как перемещать данные между листами. Я бы сделал это вот так:

    procedure TForm1.btnMoveDataClick(Sender: TObject);
    var ISheetSrc, ISheetDst: Excel8TLB._Worksheet;
    IRangeSrc, IRangeDst: Excel8TLB.Range;
    begin
    if Assigned(IWorkbook) then
    try
    ISheetSrc := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    ISheetDst :=
    IWorkbook.Worksheets.Add(EmptyParam, ISheetSrc, 1, EmptyParam, 0) as _Worksheet;
    IRangeSrc := ISheetSrc.Range[‘TestRange2’, EmptyParam];
    IRangeDst := ISheetDst.Range[‘D4’, EmptyParam];
    IRangeSrc.Copy(IRangeDst);
    finally
    IRangeDst := nil;
    IRangeSrc := nil;
    ISheetDst := nil;
    ISheetSrc := nil;
    end;
    end;

    Метод Copy интерфейса Range принимает в качестве параметра любой другой Range. Причем, совсем не важно, совпадают ли размеры источника и получателя, так как данные копируются начиная с левой верхней ячейки получателя в количестве, определенном размером источника. (О, завернул!) Для затравки хотелось бы показать код, который выполняет ту же задачу, но через буфер обмена (а вдруг в Word вставлять будем):

    procedure TForm1.btnMoveDataClick(Sender: TObject);
    var ISheetSrc, ISheetDst: Excel8TLB._Worksheet;
    IRangeSrc, IRangeDst: Excel8TLB.Range;
    begin
    if Assigned(IWorkbook) then
    try
    ISheetSrc := IWorkbook.Worksheets.Item[‘Лист1’] as Excel8TLB._Worksheet;
    ISheetDst :=
    IWorkbook.Worksheets.Add(EmptyParam, ISheetSrc, 1, EmptyParam, 0) as _Worksheet;
    IRangeSrc := ISheetSrc.Range[‘TestRange2’, EmptyParam];
    IRangeDst := ISheetDst.Range[ ‘D4’, EmptyParam];
    IRangeSrc.Copy(EmptyParam); // так кладем в Clipboard
    ISheetDst.Paste(IRangeDst, EmptyParam, 0); // а вот так достаем оттуда
    finally
    IRangeDst := nil;
    IRangeSrc := nil;
    ISheetDst := nil;
    ISheetSrc := nil;
    end;
    end;

    Проект — SampleExcel (в архиве с данной статьёй).

    Часть 5. Передача данных разного типа.

    Как и прежде, я беру проект-пример из предыдущей своей статьи и переделываю его. Напомню, что в нем используется импортированная в Delphi 4 библиотека типов Excel (правда, исходный код я пишу уже в Delphi 5). Свои примеры я тестирую с помощью Excel 2000 с установленным пакетом обновлений Service Release 1. Впрочем, я уверен, что все примеры вы сможете откомпилировать в Delphi 4 и использовать с Excel 97 SR2 и Excel 2000 без SR1. Обращаю внимание на установку SR2 для Excel 97. Это обязательное условие, так как без этого обновления Excel содержит очень неприятную ошибку, периодически возникающую при закрытии книг. Поэтому, пожалуйста, будьте внимательны, господа!

    Какие данные мы используем.

    «Всяческие»! Да, каждый из нас использует в своих приложениях все многообразие типов данных, с которыми способен справиться компилятор и операционная система. В принципе, можно было бы описать решение проблемы для всего этого «многообразия». Но! Я всегда утверждал и буду утверждать, что типы данных, для которых нельзя написать Cell.Value = NewValue, бесполезно использовать в Excel. Я не «влюблен» в Excel. Но я твердо уверен в том, что Excel в сегодняшнем его состоянии — одно из мощнейших средств анализа корпоративных данных. И я до сих пор не могу найти другого применения картинкам в книгах Excel, кроме как наведение красоты. Поэтому я остановлюсь только на способах передачи целых и вещественных чисел, строк, дат и логических значений. В общем, всего того, что так надоело нам в наш быстротекущий media-век.

    Важно! Проект-пример содержит одну форму, в обработчиках OnCreate и OnDestroy которой автоматически создается и освобождается Excel.Application. Причем, для этого я использовал методы из предыдущих примеров — CreateExcel, ShowExcel и ReleaseExcel. Особое внимание хочу обратить на ReleaseExcel, с помощью которого освобождается интерфейс Excel.Application. Если же необходимо закрыть Excel, вызывайте перед освобождением интерфейса метод Quit этого интерфейса (у себя я закомментировал эту строку). На форму я поместил таблицу (TTable) из DBDEMOS — CUSTOMER.DB. Чтобы видеть хоть что-то, я использовал для этой таблицы DBGrid и навигатор. В правой части формы вы увидите кнопку со странным названием «Send data» и группу переключателей под ней. С помощью этой группы вы сможете выбрать один из рассмотренных в этой статье вариантов передачи данных в Excel (данные я беру из вышеназванной таблицы). После выбора варианта передачи и нажатия кнопки в Excel создается новая книга на основе шаблона Test.xls — книги, которую я прилагаю вместе с проектом. В эту книгу из таблицы переносятся значения полей из всех записей. При переносе я измеряю количество миллисекунд, затраченных на этот перенос, и помещаю это количество в ячейку A1 созданной книги.

    И еще. При создании примера я старался избегать лишнего кода, который в любом другом случае добавил бы законченность алгоритмам. Меня стоило бы основательно поругать за то, что я не запоминаю закладок при проходе по таблице, использую значения полей, забывая о DisplayText, и просто переношу значения в книгу без какого-либо форматирования ячеек. Наверняка, вы найдете еще несколько моментов, которые я совсем упустил из вида. Попросту, красота кода уступила свое место желанию сосредоточиться на единственной цели — эффективной передаче данных. Единственное, что я сделал, так это избавился от возможности вмешательства пользователя в процесс переноса данных, отключив (и включив затем) у Excel свойства Interactive и ScreenUpdating, а также вызвав DisableControls для набора данных.

    Какие же варианты я предложу на суд читателя? Вариантов и возможностей передачи данных в Excel существует достаточно много — от очень «заумных» (BIFF) до экзотических (сохраним в текстовый файл и затем его откроем). Все они имеют какие-то свои достоинства и недостатки. В этой статье я расскажу об одних из самых простых и эффективных решениях этой проблемы (название раздела все-таки обязывает — HelloWorld!). Правда, первый описанный здесь способ, у меня ничего кроме зубной боли не вызывает. Итак.

    Не делайте так — клиническая смерть!

    Собственно, об этом варианте я мог бы и не писать. Во-первых, я встречал его в большинстве книг и статей, посвященных этой теме. Во-вторых, я, на самом деле, думаю, что так делать нельзя. Речь идет о тривиальном переносе данных с помощью обычного присваивания свойству ячейки Value нового значения. Тем не менее, я надеюсь что, описывая этот вариант, мне удастся обратить ваше внимание на несколько важных вещей.

    Первый переключатель на форме (с заголовком «Value :=») скрывает за собой вызов процедуры ToNewValue. Вот ее исходный код:

    procedure TForm1.ToNewValue(ISheet: IxlWorksheet);
    var Row, Column, i: integer;
    begin
    tblCust.First;
    Row := StartRow;
    tblCust.First;
    while not tblCust.EOF do begin
    Column := StartColumn;
    for i := 0 to tblCust.Fields.Count — 1 do begin
    ISheet.Cells.Item[Row, Column].Value := FieldToVariant(tblCust.Fields[i]);
    Inc(Column);
    end;
    Inc(Row);
    tblCust.Next;
    end;
    end;

    Что в ней особенного? Да, это обычный проход по всей таблице (First; while not EOF do Next;) и по всем ее полям (вложенный for). Но! Во-первых, в этом примере я всегда начинаю переносить данные с ячейки, определенной константами StartRow и StartColumn. Во-вторых, ожидаемый вами оператор присваивания «Cell.Value := Field.Value» я заменил на «Cell.Value := FieldToVariant(Field)». То есть, в отличие от классического, «из учебников», примера я использую свою функцию получения вариантного значения поля.

    Если присмотреться к исходному тексту функции FieldToVariant,

    function FieldToVariant(Field: TField): OLEVariant;
    begin
    Result := »;
    case Field.DataType of
    ftString, ftFixedChar, ftWideString, ftMemo, ftFmtMemo: Result := »» + Field.AsString;
    ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc: Result := Field.AsInteger;
    ftFloat, ftCurrency, ftBCD: Result := Field.AsFloat;
    ftBoolean: Result := Field.AsBoolean;
    ftDate, ftTime, ftDateTime: Result := Field.AsDateTime;
    end;
    end;

    то можно разглядеть причину. Кроме достаточно глупых «AsInteger», «AsFloat» и пр. я добавляю в начало значений строковых полей одиночную кавычку. Вы, наверняка, помните, что ввод в формулу ячейки первым символом одиночной кавычки заставляет Excel принимать остальные символы как текст. Но, это касается формул ячеек, а не их значений!? Попробуйте убрать добавление этой кавычки и перекомпилировать проект. Конечно, и в этом варианте все будет работать. Но (предлагаю кощунственный метод) отредактируйте поле «Company» в первой записи таблицы, введя туда строку «3/7». Не увидите ли вы в полученной книге вместо этой строки дату или результат деления (зависит от языковых настроек ОС)? Столь же некорректный результат будет получен и при попытке передачи строки «0001», которая будет воспринята как число 1. Благо, одиночная кавычка в начале строки решает эту проблему даже при присваивании в Value (а не в Formula).

    Впрочем, я не намерен долго останавливаться на этом варианте. Все дело в значении ячейки A1 в полученной книге. На моем компьютере перенос всей таблицы занял более 4 секунд. И это на не более полусотне записей. А что было бы, если бы количество записей перевалило за пару десятков тысяч? Кстати, повторные запуски показали колебание этого времени в пределах от трех до пяти секунд. Думаю, что такие колебания были связаны только с файловым кэшем ОС. Так что, «Не делайте так!»

    Больному уже лучше. Правда, он все еще в реанимации.

    На что же уходит время в предыдущем варианте? Все просто! Львиная доля времени уходит на вызовы интерфейсов внешнего COM-сервера. И, не смотря на то, что мы используем ранее связывание с библиотекой типов, это так. Еще мой любимый классик (Калверт, знаете ли) писал о нетерпимости к вызовам интерфейсов внешних OLE-серверов в больших циклах. Как видите, классик прав.

    Наша задача — избавиться от вызова Cell.Value в цикле. И это решаемо с помощью вариантных массивов. Вот так:

    procedure TForm1.ToVarArray(ISheet: IxlWorksheet);
    var Row, Column, i: integer;
    IR1, IR2: IxlRange;
    Arr: OLEVariant;
    begin
    Arr := VarArrayCreate([1, tblCust.RecordCount, 1, tblCust.Fields.Count], varVariant);
    Row := 1;
    tblCust.First;
    while not tblCust.EOF do begin
    Column := 1;
    for i := 0 to tblCust.Fields.Count — 1 do begin
    Arr[Row, Column] := FieldToVariant(tblCust.Fields[i]);
    Inc(Column);
    end;
    Inc(Row);
    tblCust.Next;
    end;
    IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
    IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount — 1,
    StartColumn + tblCust.Fields.Count — 1];
    ISheet.Range[IR1, IR2].Value := Arr;
    end;

    Здесь я использую вариантный массив Arr, который предварительно создается с размерами таблицы (количество записей на количество полей). Благо Microsoft построила очень четкую схему работы с вариантными массивами и интерфейсами, их «понимающими» (этим и пользуюсь). Из кода видно, что я по-прежнему прохожу всю таблицу, запоминая в элементах массива значения полей, полученных из вышеописанной функции FieldToVariant. Мы, ведь, снова используем варианты, и проблема строки «3/7» остается. Последние три строки процедуры позволяют получить верхнюю левую и нижнюю правую ячейки области, в которую будут перенесены данные. А, затем, одним присваиванием в «Область.Value» я переношу данные из массива в ячейки этой области. Хорош способ, не правда ли? Код максимально прост. Время, полученное в ячейке A1 на порядок меньше. Правда, есть несколько проблем.

    Главное, что бросилось бы в глаза опытного Delphi-разработчика, это создание массива в начале процедуры. Известно ли количество записей SQL-запроса после его открытия? Не всегда (FechAll). Хорошо, можно создать пустой массив и делать ему VarArrayRedim. Вряд ли! Так как количество записей — есть первое измерение вариантного массива (необходимо здесь тире или нет. ). А я не нашел до сих пор способа изменить первую размерность вариантного массива при наличии второй. Может, кто подскажет начинающему (про начинающего — правда). Возможно, было бы правильно создать массив массивов (понимаете о чем я?). Но, что-то не заладилось там, Наверху. Поэтому такое решение не проходит. Точнее проходит, но как-то не очень хорошо — попробуйте!

    Тем не менее, этот вариант вполне «живуч» при осторожном его использовании и на небольших объемах данных. Скорость нормальная, проблем с «3/7» нет. В общем, больной будет жить!

    Часть 6. Передача данных используя буфер обмена и DDE.
    Clipboard.

    Совсем недавно мне пришло очередное гневное послание на тему буфера обмена — Clipboard. Объясню. XL Report очень долго использовал только буфер обмена для передачи данных из приложения в Excel. Дело в том, что при таком варианте (а я его здесь опишу) достигается практически максимальная скорость переноса данных. Дело в том, что в буфер обмена «кладется» длинная строка, содержащая строковые значения полей набора данных (AsString), разделенные символом табуляции. Записи отделяются друг от друга переводом строки (#10). Собственно, этот формат известен в научных кругах как CSV (разделитель между значениями). Долгое время это меня устраивало, пока XL Report использовался только нашими разработчиками и ограниченным кругом клиентов фирмы. Но тут мы решили выложить это решение в Сеть. И.

    Каюсь. Я никак не беспокоился за сохранность содержимого буфера обмена. Понятное дело, это не очень правильно. Но, так было. Так есть и сейчас. Правда, по другим причинам. Для того, чтобы «выжать» из Excel максимальное быстродействие, приходится использовать определенные методы и свойства его интерфейсов. А их использование не оставляет ничего, кроме как уничтожение содержимого буфера обмена. В общем, сейчас я покажу максимально возможное (в этой статье. ) решение по переносу данных — CSV. Итак:

    procedure TForm1.ToCSV(ISheet: IxlWorksheet);
    var i: integer;
    IR1, IR2: 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

    Как я и писал выше, в строковый буфер Buff собирается вся таблица. Строковые значения полей я разделяю символом табуляции, а в «конце» записи добавляю перевод строки. Все значения я заключаю дополнительно в двойные кавычки. Затем вызовом процедуры BufferToClipboard я помещаю содержимое этой переменной в буфер обмена и делаю «хитрый» вызов PasteSpecial для области, в которую будут помещены данные. Вот и все?! Нет, есть еще проблемы.

    Во-первых, процедура BufferToClipboard — вещь не стандартная. Она создана как альтернатива методу SetTextBuf класса TClipboard. Наверняка, все знают что в VCL доступна глобальная переменная Clipboard, экземпляр класса TClipboard, инкапсулирующего свойства и методы доступа к этому самому буферу обмена. И, собственно, вызов SetTextBuf позволяет поместить строку в буфер. Но!

    SetTextBuf помещает в буфер обмена текст в формате CF_TEXT — обычный текст с однобайтовым представлением символов, что не есть хорошо. Точнее, это совсем не хорошо, если вы работаете с «русскими буквами» на разных операционных системах от MS, причем, разных с точки зрения локализации. Именно тогда и возникают у пользователей вопросы при попытке прочитать некий набор «закорючек», отдаленно напоминающих письмена племени зибару. Поэтому я предпочитаю UNICODE, вставка которого в буфер обмена и реализована в этой процедуре. Ее текст я не буду приводить в этой статье, так как это потребует лишних для этого объяснений. Сама процедура присутствует в исходных текстах проекта-примера. Поэтому знатоков этого дела отсылаю к ним, а таким начинающим, как и я, просто рекомендую ее использовать в последующих своих разработках (при необходимости).

    UNICODE — это первая проблема, которая была решена. Но при использовании CSV есть и другие. И, главная, из них — «3/7». Без вмешательства в содержимое поля (аналогично добавлению одиночной кавычки при вариантах) ее нельзя обойти никак. Я добавляю пробел. Да, тривиального пробела вполне хватает для решения этой проблемы. И замеченная вами функция FieldToStr как раз это и делает.

    По поводу «хитрого» PasteSpecial. При «правильном» вызове метода PasteSpecial интерфейса Range я столкнулся с неразрешимыми проблемами. Поэтому я привел к варианту область и заставил ОС и сам Excel самостоятельно разбираться с тем, что и с какими параметрами я вызываю. Часто, при разработке с библиотеками типов от MS такой ход экономит время и, главное, сохраняет здоровье и нормальное состояние нервной системы. Не используйте его все время, но и не пренебрегайте им. Итак, при использовании CSV и буфера обмена мы достигли «громаднейшей» скорости передачи данных, обойдя при этом несколько проблем.

    Недостатки, которые заметят критики и просто опытные разработчики. Переданное строковое значение «0001» предстает перед изумленным пользователем числом 1. Это можно обойти только через предварительное форматирование конкретной ячейки в текстовый формат. Подчеркиваю, предварительно, то есть перед переносом данных. И, по-прежнему, содержимое буфера обмена не сохраняется. Более того, оно затем нагло очищается вызовом Clipboard.Clear. «Мы его теряем!»

    Думаю, вы уже заметили, что один из переключателей вариантов имеет название, состоящее из зловещего сочетания букв — 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

    Все реже я встречаю книги, где было бы описано взаимодействие приложений посредством 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 (в архиве с данной статьёй).

    Часть 7: Пример обмена данными с Excel используя VCL и OLE.

    Автор: Bird Feniks.

    В Delphi 5 и выше, для обмена данными между Вашим приложением и Excel можно использовать компонент TExcelApplication, доступный на Servers Page в Component Palette.

    Создаем новый проект. На форму кладем TStringGrid, заполняем его некоторыми данными и добавляем две кнопки, с названиями «To Excel» и «From Excel». Так же на форму кладем компонент TExcelApplication и в свойстве «Name» указываем «XLApp», а свойство «ConnectKind» меняем на «ckNewInstance».

    Когда нам необходимо работать с Excel, то обычно мы открываем ExcelApplication, затем открываем WorkBook и в конце используем WorkSheet.

    Итак, несомненный интерес представляет для нас листы (WorkSheets) в книге (WorkBook). Давайте посмотрим как всё это работает.

    Посылка данных в Excel.

    Это можно сделать с помощью следующей процедуры :

    procedure TForm1.BitBtnToExcelOnClick(Sender: TObject);
    var
    WorkBk : _WorkBook; // определяем WorkBook
    WorkSheet : _WorkSheet; // определяем WorkSheet
    I, J, K, R, C : Integer;
    IIndex : OleVariant;
    TabGrid : Variant;
    begin
    if GenericStringGrid.Cells[0,1] <> » then
    begin
    IIndex := 1;
    R := GenericStringGrid.RowCount;
    C := GenericStringGrid.ColCount;
    // Создаём массив-матрицу
    TabGrid := VarArrayCreate([0,(R — 1),0,(C — 1)],VarOleStr);
    I := 0;
    // Определяем цикл для заполнения массива-матрицы
    repeat
    for J := 0 to (C — 1) do
    TabGrid[I,J] := GenericStringGrid.Cells[J,I];
    Inc(I,1);
    until
    I > (R — 1);

    // Соединяемся с сервером TExcelApplication
    XLApp.Connect;
    // Добавляем WorkBooks в ExcelApplication
    XLApp.WorkBooks.Add(xlWBatWorkSheet,0);
    // Выбираем первую WorkBook
    WorkBk := XLApp.WorkBooks.Item[IIndex];
    // Определяем первый WorkSheet
    WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
    // Сопоставляем Delphi массив-матрицу с матрицей в WorkSheet
    Worksheet.Range[‘A1’,Worksheet.Cells.Item[R,C]].Value := TabGrid;
    // Заполняем свойства WorkSheet
    WorkSheet.Name := ‘Customers’;
    Worksheet.Columns.Font.Bold := True;
    Worksheet.Columns.HorizontalAlignment := xlRight;
    WorkSheet.Columns.ColumnWidth := 14;
    // Заполняем всю первую колонку
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].Font.Color := clBlue;
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].HorizontalAlignment := xlHAlignLeft;
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].ColumnWidth := 31;
    // Показываем Excel
    XLApp.Visible[0] := True;
    // Разрываем связь с сервером
    XLApp.Disconnect;
    // Unassign the Delphi Variant Matrix
    TabGrid := Unassigned;
    end;
    end;

    Получение данных из Excel.

    Это можно сделать с помощью следующей процедуры :

    procedure TForm1.BitBtnFromExcelOnClick(Sender: TObject);
    var
    WorkBk : _WorkBook;
    WorkSheet : _WorkSheet;
    K, R, X, Y : Integer;
    IIndex : OleVariant;
    RangeMatrix : Variant;
    NomFich : WideString;
    begin
    NomFich := ‘C:MyDirectoryNameOfFile.xls’;
    IIndex := 1;
    XLApp.Connect;
    // Открываем файл Excel
    XLApp.WorkBooks.Open(NomFich,EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam,
    EmptyParam,EmptyParam,
    EmptyParam,EmptyParam,0);
    WorkBk := XLApp.WorkBooks.Item[IIndex];
    WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
    // Чтобы знать размер листа (WorkSheet), т.е. количество строк и количество
    // столбцов, мы активируем его последнюю непустую ячейку
    WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
    // Получаем значение последней строки
    X := XLApp.ActiveCell.Row;
    // Получаем значение последней колонки
    Y := XLApp.ActiveCell.Column;
    // Определяем количество колонок в TStringGrid
    GenericStringGrid.ColCount := Y;
    // Сопоставляем матрицу WorkSheet с нашей Delphi матрицей
    RangeMatrix := XLApp.Range[‘A1’,XLApp.Cells.Item[X,Y]].Value;
    // Выходим из Excel и отсоединяемся от сервера
    XLApp.Quit;
    XLApp.Disconnect;
    // Определяем цикл для заполнения TStringGrid
    K := 1;
    repeat
    for R := 1 to Y do
    GenericStringGrid.Cells[(R — 1),(K — 1)] := RangeMatrix[K,R];
    Inc(K,1);
    GenericStringGrid.RowCount := K + 1;
    until
    K > X;
    // Unassign the Delphi Variant Matrix
    RangeMatrix := Unassigned;
    end;

    Ниже представлен пример создания новой таблицы в Excel 2000:

    uses
    ComObj, ActiveX;

    var
    Row, Col: integer;
    DestRange: OleVariant;
    Excel: Variant;

    begin
    Excel := CreateOleObject(‘Excel.Application.9’);
    Excel.Visible := True;
    Excel.WorkBooks.Add; //Создать новую таблицу

    //Можно помещать текст и значения в диапазон ячеек
    //Поместить слово тест в диапазон ячеек
    Excel.ActiveSheet.Range[‘A2’, ‘B3’].Value := ‘Тест’;
    //Или число
    Excel.ActiveSheet.Range[‘A4’, ‘B5’].Value := 42;

    //А вот так задаётся формула
    Excel.ActiveSheet.Range[‘A10’, ‘A11’].Formula := ‘=RAND()’;

    //Можно задавать номера ячеек и столбцов
    Excel.ActiveSheet.Cells.Item[1, 1].Value := ‘Первая ячейка’;

    Row:=1;
    Col:=3;
    Excel.ActiveSheet.Cells.Item[Row, Col].Value := ‘Другая ячейка’;

    //Можно скопировать данный из одного диапазона ячеек в другой
    DestRange := Excel.Range[‘D6’, ‘F10’];
    Excel.Range[‘A1’, ‘C5’].Copy(DestRange);

    //Можно задавать параметры шрифта в определённой ячейке
    Excel.Range[‘A2’, ‘A2’].Font.Size := 20;
    Excel.Range[‘A2’, ‘A2’].Font.FontStyle := ‘Bold’;
    Excel.Range[‘A2’, ‘A2’].Font.Color := clFuchsia;
    Excel.Range[‘A2’, ‘A2’].Font.Name := ‘Arial’;

    //Можно ещё и так изменить цвет диапазона ячеек
    Excel.Range[‘B2’, ‘C6’].Interior.Color := RGB(223, 123, 123);

    Далее представлен пример открытия и закрытия таблицы:

    uses
    ComObj, ActiveX;

    var
    Excel: Variant;
    WBk : OleVariant;
    SaveChanges: OleVariant;

    begin
    Excel := CreateOleObject(‘Excel.Application.9’);
    Excel.Visible := True;

    //Открыть существующую таблицу
    WBk := Excel.WorkBooks.Open(‘C:Test.xls’);

    WBk.Close(SaveChanges := True);
    Excel.Quit;

    Статья добавлена: 8 мая 2005

    Зарегистрируйтесь/авторизируйтесь,
    чтобы оценивать статьи.

    Статьи, похожие по тематике

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

    Ссылка для форумов (BBCode):

    Быстрая вставка ссылки на статью в сообщениях на сайте:
    <> (буква a — латинская) — только адрес статьи (URL);
    <<статья:10>> — полноценная HTML-ссылка на статью (текст ссылки — название статьи).

    Поделитесь ссылкой в социальных сетях:

    Комментарии читателей к данной статье


    Репутация: нет

    Репутация: нет

    Репутация: +40

    Репутация: нет

    Оставлять комментарии к статьям могут только зарегистрированные пользователи.

    Источник

    Adblock
    detector

      msm.ru

      Нравится ресурс?

      Помоги проекту!

      !
      user posted image

      Пожалуйста, выделяйте текст программы тегом [сode=pas] … [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

      Соблюдайте общие правила форума

      Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
      1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
      2. Как «свернуть» программу в трей.
      3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
      4. Как запустить программу/файл? (и дождаться ее завершения)
      5. Как перехватить API-функции, поставить hook? (перехват сообщений от мыши, клавиатуры — внедрение в удаленное адресное прстранство)
      … (продолжение следует) …


      Внимание:
      Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
      Повторная попытка — 60 дней. Последующие попытки — бан.
      Мат в разделе — бан на три месяца…


      Полезные ссылки:
      user posted image MSDN Library user posted image FAQ раздела user posted image Поиск по разделу user posted image Как правильно задавать вопросы


      Выразить свое отношение к модераторам раздела можно здесь: user posted image Rouse_, user posted image Krid

      >
      Delphi и Excel 2007

      • Подписаться на тему
      • Сообщить другу
      • Скачать/распечатать тему



      Сообщ.
      #1

      ,
      28.12.07, 07:53

        Здравствуйте!
        у меня такая проблема…в 2003 офисе этот кусок нормально работает:

        ExpandedWrap disabled

          VSheet3.ChartObjects[2].Chart.SetSourceData(

            VSheet2.Range[VSheet2.Cells[countshop + 5,4],

            VSheet2.Cells[countshop + 5,RowItogo-1]], xlRows);

        но в 2007 ругается, что что Член группы не найден

        Подскажите пожалуйста в чём проблема и как её решить.

        Заранее признателен


        RuSA



        Сообщ.
        #2

        ,
        28.12.07, 14:06

          Фактически здесь три COM-вызова.
          Их надо поочереди выполнить и сразу станет ясно на какой именно операции проблема.

          ExpandedWrap disabled

               {  var X1, X2: OleVariant; }

               X1 := VSheet2.Range[VSheet2.Cells[countshop + 5,4];

               X2 := VSheet2.Cells[countshop + 5,RowItogo-1];

               VSheet3.ChartObjects[2].Chart.SetSourceData( X1, X2, xlRows);

          Возможно индекс неверен или что-либо из этой серии.

          Добавлено 28.12.07, 14:08
          Даже не больше чем три: вместо X1 := … еще детальнее сделать :

          ExpandedWrap disabled

               X1 := VSheet2.Cells[countshop + 5,4];

               X1 := VSheet2.Range[ X1 ];


          ScoobyDoo



          Сообщ.
          #3

          ,
          29.12.07, 06:21

            Уважаемый RuSA!
            разбил этот кусок как Вы посоветовали, ошибку выдаёт на

            ExpandedWrap disabled

              X1 := VSheet2.Range[ X1 ];

            OLE error 800A03EC

            подскажите пожалуйста как это исправить…в 2003 офисе же работает

            Добавлено 29.12.07, 06:24
            хотя в 2003 офисе при данном разбитии операторов именно на этом же месте вылазиет та же самая ошибка….может как то неверно разбито?


            Galiaf



            Сообщ.
            #4

            ,
            29.12.07, 07:12

              ребята если я не ошибаюсь то 2007 очень сильно отличается от 2003 даже форматы файлов не совместимы…


              RuSA



              Сообщ.
              #5

              ,
              29.12.07, 15:26

                Цитата Galiaf @ 29.12.07, 07:12

                ребята если я не ошибаюсь то 2007 очень сильно отличается от 2003 даже форматы файлов не совместимы…

                Похоже.
                Тогда можно сделвать так:
                1) самое простое — создать аналогичный код на встроенном в Excel VBScript (макросы/редактировать …),
                затем перевести этот код на Delphi.
                2) найти интерфейсы для Office2007 (наподобии файла из поставки Delphi «%Delphi%OcxServersOffice2000.pas» и работать
                через них, а не через IDispatch.

                Master

                jack128



                Сообщ.
                #6

                ,
                18.01.08, 13:57

                  M

                  lidhen
                  не лезь в чужую ветку со своими вопросами


                  onyx



                  Сообщ.
                  #7

                  ,
                  18.01.08, 14:42

                    lidhen, по окуратнее на поворотах! Новую тему создать, неужели так сложно?


                    DimaBy



                    Сообщ.
                    #8

                    ,
                    21.01.08, 07:21

                      Цитата Galiaf @ 29.12.07, 07:12

                      ребята если я не ошибаюсь то 2007 очень сильно отличается от 2003 даже форматы файлов не совместимы…

                      Не очень он отличается. Писал приложение, формирующее отчет в Excell. Отчет довольно серьезный. А кроме этого всё форматирование хранится не в отдельном шаблоне, а создается программно (знаю, извращение, но тогда так надо было) .
                      Так вот — когда запустил программу на компьютере с 2007-м офисом — всё отлично заработало с 1-го раза.

                      Добавлено 21.01.08, 07:38
                      вместо этого

                      ExpandedWrap disabled

                         X1 := VSheet2.Cells[countshop + 5,4];

                         X1 := VSheet2.Range[ X1 ];

                      лучше использовать вот это

                      ExpandedWrap disabled

                         X1 := VSheet2.Cells[countshop + 5,4];

                         X1 := VSheet2.Range[ X1,X1 ];

                      Я вообще не знаю, как с одним аргументом работало.


                      ScoobyDoo



                      Сообщ.
                      #9

                      ,
                      29.01.08, 06:55

                        Цитата ScoobyDoo @ 28.12.07, 07:53

                        Здравствуйте!
                        у меня такая проблема…в 2003 офисе этот кусок нормально работает:

                        ExpandedWrap disabled

                          VSheet3.ChartObjects[2].Chart.SetSourceData(

                            VSheet2.Range[VSheet2.Cells[countshop + 5,4],

                            VSheet2.Cells[countshop + 5,RowItogo-1]], xlRows);

                        но в 2007 ругается, что что Член группы не найден

                        Подскажите пожалуйста в чём проблема и как её решить.

                        Заранее признателен

                        это проблема решилась просто…надо было только у ChartObjects[2] квадратные скобки заменит на круглые.

                        дальше возникла следующая проблема:

                        ExpandedWrap disabled

                          v5 := v1.Range[v1.Cells[HomeRow-1,HomeColumn], v1.Cells[HomeRow-1,Column-1]];

                              for i := 1 to 5 do

                                begin

                                  Form1.App.Charts[i].SeriesCollection.[B]Item[1][/B].XValues := v5;

                                  Form1.App.Charts[i].HasLegend := False;

                                end;

                        этот отрывок кода выдаёт ту же ошибку Член группы не найден.

                        Путём разбиения определил, что ругается именно на выделенный жирным Item[1].
                        Если меняю скобки на круглые, то ошибка меняется на несовпадение типов данных.

                        Где то в инете увидел, что можно использовать без итема следующим образом

                        ExpandedWrap disabled

                          Form1.App.Charts[i].SeriesCollection(1).XValues := v5;

                        но так же в случае круглых скопок ошибка не совпадения типов, а при [] — неверный член группы.

                        Помогите, пожалуйста, решить эту проблему. Опять же в 2003 офисе исходный код работает.


                        ScoobyDoo



                        Сообщ.
                        #10

                        ,
                        01.02.08, 08:35

                          Помогите пожалуйста решить эту проблему :(


                          ScoobyDoo



                          Сообщ.
                          #11

                          ,
                          05.02.08, 11:30

                            Неужели никто из знающих не читает эту ветку? уже не знаю что можно попробовать


                            RuSA



                            Сообщ.
                            #12

                            ,
                            06.02.08, 01:25

                              Цитата ScoobyDoo @ 01.02.08, 08:35

                              Помогите пожалуйста решить эту проблем

                              Уже отвечали — например, см. сообщ. #5


                              ScoobyDoo



                              Сообщ.
                              #13

                              ,
                              06.02.08, 05:19

                                Цитата RuSA @ 06.02.08, 01:25

                                Цитата ScoobyDoo @ 01.02.08, 08:35

                                Помогите пожалуйста решить эту проблем

                                Уже отвечали — например, см. сообщ. #5

                                ну я и следовал первому пункту в 5м посте…при редактировании макроса тоже самое нормально работает…а в дельфие нет :(


                                RuSA



                                Сообщ.
                                #14

                                ,
                                06.02.08, 12:12

                                  Цитата ScoobyDoo @ 29.01.08, 06:55

                                  … в случае круглых скопок ошибка не совпадения типов,

                                  так надо проверить в каком месте несовпадение. Возможно некорректно присваивается значение для XValues !
                                  Можно проверить чтением:

                                  ExpandedWrap disabled

                                       {xV: OleVariant; }

                                       xV :=  Form1.App.Charts[i].SeriesCollection.Item(0).XValues;


                                  ScoobyDoo



                                  Сообщ.
                                  #15

                                  ,
                                  07.02.08, 09:09

                                    Цитата RuSA @ 06.02.08, 12:12

                                    Цитата ScoobyDoo @ 29.01.08, 06:55

                                    … в случае круглых скопок ошибка не совпадения типов,

                                    так надо проверить в каком месте несовпадение. Возможно некорректно присваивается значение для XValues !
                                    Можно проверить чтением:

                                    ExpandedWrap disabled

                                         {xV: OleVariant; }

                                         xV :=  Form1.App.Charts[i].SeriesCollection.Item(0).XValues;

                                    Извините, не понял…как проверить чтением?
                                    закомментировал

                                    ExpandedWrap disabled

                                      Form1.App.Charts[i].SeriesCollection.[B]Item[1][/B].XValues := v5;

                                      Form1.App.Charts[i].HasLegend := False;

                                    и вставил

                                    ExpandedWrap disabled

                                      xV :=  Form1.App.Charts[i].SeriesCollection.Item(0).XValues;

                                    — выдало ошибку что то типа неверной адресации.
                                    заменил 0 на 1:

                                    ExpandedWrap disabled

                                      xV :=  Form1.App.Charts[i].SeriesCollection.Item(1).XValues;

                                    — работает нормально, без ошибок…но что это даёт?

                                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

                                    0 пользователей:

                                    • Предыдущая тема
                                    • Delphi: Система, Windows API
                                    • Следующая тема

                                    Рейтинг@Mail.ru

                                    [ Script execution time: 0,0674 ]   [ 16 queries used ]   [ Generated: 13.04.23, 22:37 GMT ]  

                                    Студворк — интернет-сервис помощи студентам

                                    Здравствуйте!
                                    Если что-то неправильно (не та ветка и т.д.), то я первый раз на форуме.
                                    Пишу программу, работающую с Excel. Сохраняет-загружает нормально, большинство ошибок исправил. Но когда жмёшь кнопку «Сохранить в Excel» и одновременно открыт файл, в который нужно сохранить, то выдаёт ошибку «Project ИМЯ_ТАКОЕ-ТО.exe raised exception class EOleException with message ‘Нет доступа к ИМЯ_ТАКОЕ-ТО.xlsx». Process stopped. Use Step or Run to continue», зелёная стрелка указывает на строку Excel.WorkBooks[1].WorkSheets[1].SaveAs(GetExcelFileName), выделенную синим цветом и Excel остаётся в фоновых процессах Диспетчера задач.

                                    Если без Delphi запускаю из Windows скомпилированный exe-файл и одновременно открыт файл, куда нужно сохранить, тогда тоже выдаёт ошибку ‘Нет доступа к ИМЯ_ТАКОЕ-ТО.xlsx’.
                                    Есть ли что-то вроде Excel.DisplayAlerts := False, чтобы перехватить эту ошибку и либо закрыть Excel (необязательно. Пользователь открыл — пусть сам и закрывает), либо вывести вежливый ShowMessage с просьбой закрыть файл вместо ошибки «Нет доступа»?
                                    Помогите, пожалуйста. Уже несколько дней эту проблему ищу. Спасибо!

                                    PS: попробовал хитрость — сохранять с другим расширением файла, чтобы пользователь просто не пробовал его открывать. Но какая-то эта хитрость… нечестная.

                                    На всякий случай программный код кнопки «Сохранить в Excel»:

                                    Delphi
                                    1
                                    2
                                    3
                                    4
                                    5
                                    6
                                    7
                                    8
                                    9
                                    10
                                    11
                                    12
                                    13
                                    14
                                    15
                                    16
                                    17
                                    18
                                    19
                                    20
                                    21
                                    22
                                    23
                                    24
                                    25
                                    26
                                    27
                                    28
                                    29
                                    
                                    procedure TRiskForm.SaveButtonClick(Sender: TObject);
                                    var
                                      i, j: Word;
                                    begin
                                      try
                                      Excel := CreateOleObject('Excel.Application');
                                     
                                        Excel.Visible := false;
                                        Excel.Workbooks.Add;
                                        Excel.ActiveWorkbook.Worksheets[1];
                                     
                                      with ConditionOfBankStringGrid do
                                      begin
                                        for i := 0 to ColCount - 1 do
                                          for j := 0 to RowCount - 1 do
                                            Excel.Sheets[1].Cells[j + 1, i + 1] := Cells[i, j];
                                      end;
                                        Excel.Sheets[1].Cells[1, 7] := 'Итог:'; {Записываем процент РВКа}
                                        Excel.Sheets[1].Cells[1, 8] := ProbabilityLabel.Caption;
                                        Excel.DisplayAlerts := False; // Отключаем все предупреждения Excel
                                     
                                        Excel.WorkBooks[1].WorkSheets[1].SaveAs(GetExcelFileName);
                                        ShowMessage('Сохранено в файл "ConditionOfBank.xlsx"');
                                      finally
                                        Excel.Workbooks.Close;  // Закрываем рабочую книгу Excel
                                        Excel.Quit;             // Закрываем приложение Excel
                                        Excel := Unassigned;    // Чтобы Excel исчезал из процессов Диспетчера задач
                                      end;
                                    end;

                                    Добавлено через 19 минут
                                    Многострочные коды пробовал добавлять, но много багов получается. Пожалуйста, попроще, если есть.

                                    В устаревшем приложении Delphi-7 для экспорта данных в Excel используется TExcelApplication. Перенося это на Rio, Delphi посылает смешанные сигналы относительно доступности этого компонента.

                                    Когда компонент помещается в форму, IDE сообщает, что TExcelComponent «не найден», и открывается паскаль-код, но представление формы (F12) недоступно. Однако проект ДЕЙСТВИТЕЛЬНО компилируется, и доступ к компоненту (для открытия файлов Excel и т. Д.) Работает во время выполнения.

                                    Когда я удаляю компонент из формы и динамически создаю его в FormCreate, код ДЕЙСТВИТЕЛЬНО компилируется, но во время выполнения код генерирует ошибку: «TExcelApplication не может быть найден»

                                    «Использует Excel2000» в коде.

                                    Некоторые поисковые запросы подсказали мне, что мне нужно установить пакет Office в Delphi (см. Снимок экрана). Действительно, галочка «MS-Office2000…» была отключена, поэтому я включил ее и сохранил; однако это не помогло. При щелчке здесь «Компоненты» действительно отображается компонент TExcelApplication. Однако установка этого флажка (включая выход из Delphi и сборку проекта) не помогает (та же проблема).

                                    https://gyazo.com/960eb8b53b891a5cc320e6866513c41a

                                    Файл bpl в подкаталоге embarcadero программных файлов не существует в моей системе (и нет, я не удалял его оттуда или что-то в этом роде; я никогда не был там раньше, и там много других bpl).

                                    Итак, я немного напуган … как мне заставить что-то простое, например, экспорт данных в Excel, снова заработать?

                                    Заранее спасибо, Юр.

                                    2 ответа

                                    На скриншоте видно, что вы используете Delphi XE6, и автоматизация Excel должна нормально работать с этим. Однако тот факт, что вы используете TWordApplication и т. Д., Компоненты оболочки, может скрывать реальную причину вашей проблемы.

                                    Возможно, проблема связана с настройкой Excel, а не с настройкой Delphi.

                                    Пожалуйста, попробуйте это:

                                    • Создайте новое приложение Delphi, которое использует ComObj и пытается сделать
                                        ExcelApp := CreateOleObject('Excel.Application')
                                    
                                    • компилировать и запускать

                                    Если вы получаете сообщение об ошибке «Интерфейс не поддерживается», вероятно, это вызвано проблемой, описанной в этом ответе, и ее можно исправить. от правки реестра, объясненной там. Цитируется совет Microsoft

                                    Откройте редактор regedit. .Open HKEY_CLASSES_ROOT >> TypeLib >> {00020813-0000-0000-C000-000000000046} (ключ PIA Excel: {00020813-0000-0000-C000-000000000046}

                                    Удалите папку последней [sic, latest] версии (Excel 2013 — 1.8)


                                    0

                                    MartynA
                                    15 Июл 2020 в 12:11

                                    в процессе

                                    1. ExcelXP, Excel2000, Excel97 — эти устройства представляют собой просто отшлифованные внутренние версии стандартного импорта ActiveX. Вы можете использовать их, или вы всегда можете сделать собственный импорт, используя стандартное меню «Компоненты / Импорт Active X».

                                    Динамическое связывание, как показано в ответе MArtynA, тоже может работать, но оно лишит вас возможности проверки синтаксиса во время компиляции.

                                    var Excel: Variant; // or OleVariant
                                    ...
                                       Excel := CreateOleObject('Excel.Application');
                                    

                                    Если вы неправильно напечатали какой-либо идентификатор или использовали свойство/метод, которые еще не поддерживаются или больше не поддерживаются в вашей версии Excel, вы узнаете об этом только тогда, когда ваше приложение попытается его использовать, компиляция не предупредит вас.

                                    1. runtime the code generates an error: “TExcelApplication cannot be found” означает, что вы нарушили потоковую передачу DFM. В частности, функция ReadComponent, анализирующая ресурс DFM вашего приложения, обнаруживает, что необходимо загрузить класс с именем ‘TExcelApplication’, но такого класса в словаре нет. Используйте RegisterClass(TExcelApplication); в разделе инициализации устройства. Или переместите объявление объекта обратно в раздел published вашего класса формы — тогда Delphi RTL прозрачно выполнит этот вызов за вас.

                                    2. «IDE сообщает, что TExcelComponent« не найден »» и «установить пакет Office в Delphi» — действительно, см. Ошибка при открытии файла dfm — класс xxxx не найдено

                                    «Файл bpl в подкаталоге embarcadero программных файлов не существует в моей системе» — если бы это было так, вы не смогли бы установить флажок, потому что это как раз пытается загрузить BPL времени разработки в вашу среду IDE. И это дало бы вам совсем другую ошибку, о невозможности бездельничать ваш BPL.

                                    Лично я никогда не устанавливаю Delphi в Program Files, потому что какой-то старый код может выйти из строя с длинными и содержащими пробелы путями, а также потому, что я предпочитаю иметь возможность возиться со средствами разработки без проблем с UAC (контроль доступа пользователей Windows Vista +), включая свою виртуальную теневую файловую систему (% LocalAppData% VirtualStore).

                                    В моей установке XE2 исходные файлы .pas лежат в c:RAD Studio9.0OCX, а c:RAD Studio9.0bin имеет ТРИ dcl*office*.bpl — для Office 2000, XP и 2010.

                                    Вы уверены, что у вас нет ни того, ни другого? Затем поищите подобные файлы на всем своем диске. Все еще не найдено — запустите установку Delphi, «Изменить» и убедитесь, что вы установили образцы компонентов автоматизации Office на самом деле. Попробуйте использовать пакеты OfficeXP или Office2010 — они должны быть обратно совместимы с Office 2000 в соответствии с рекомендациями Microsoft COM.

                                    Если все еще не найдено, вы можете сделать Компоненты / Импорт компонентов / Импорт ActiveX для Excel.

                                    1. Кроме того, мне интересно увидеть RxLib в вашем списке. Официально RxLib закончился с Delphi 5 и никогда не поддерживал Delphi6 +, вместо этого жертвуя проекту JediVCL. Существовали неофициальные патчи (например, Polaris Software), но мне интересно, вложили ли они много поддержки в Unicode-версии Delphi. Даже J.E.D.I. сегодня остро не хватает рабочей силы.

                                    2. И еще раз, если вам нужно полноценное взаимодействие с Excel. Это дает вам доступ на уровне VBA к внутреннему дереву объектов Excel. Но это обременяет ваших пользователей необходимостью покупать и устанавливать Excel, а также довольно медленно. Если вы используете только ограниченное подмножество функций книги Excel, создание файлов XLSX в вашей программе может быть как быстрее, так и компактнее. По крайней мере, так мы перешли в нашем приложении. Теперь пользователи могут экспортировать электронные таблицы независимо от того, установлен ли Excel или какой-либо другой офис или вообще не установлен, и делать это намного быстрее.


                                    1

                                    Arioch ‘The
                                    15 Июл 2020 в 14:16

                                    Понравилась статья? Поделить с друзьями:
                                  • Delphi excel название листа
                                  • Delphi excel лист ячейка
                                  • Delphi excel копирование листа
                                  • Delete data in excel
                                  • Delete columns in word