уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Это первая статья из цикла «Работа с Excel в Delphi«

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

Введение

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

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

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

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

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

uses ComObj, ActiveX, Variants, Windows, Messages, SysUtils, Classes;

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

var MyExcel: OleVariant;

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

const ExcelApp = 'Excel.Application';

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

function CheckExcelInstall:boolean;
var
  ClassID: TCLSID;
  Rez : HRESULT;
begin
// Ищем CLSID OLE-объекта
  Rez := CLSIDFromProgID(PWideChar(WideString(ExcelApp)), ClassID);
  if Rez = S_OK then  // Объект найден
    Result := true
  else
    Result := false;
end;

Или ещё короче:

function CheckExcelInstall: boolean;
var
  ClassID: TCLSID;
begin
  Result:=CLSIDFromProgID(PWideChar(WideString(ExcelApp)), ClassID) = S_OK;
end;

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

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

2. Определяем запущен ли Excel

function CheckExcelRun: boolean;
begin
  try
    MyExcel:=GetActiveOleObject(ExcelApp);
    Result:=True;
  except
    Result:=false;
  end;
end;

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

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

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

function RunExcel(DisableAlerts:boolean=true; Visible: boolean=false): boolean;
begin
  try
{проверяем установлен ли Excel}
    if CheckExcelInstall then
      begin
        MyExcel:=CreateOleObject(ExcelApp);
//показывать/не показывать системные сообщения Excel (лучше не показывать)
        MyExcel.Application.EnableEvents:=DisableAlerts;
        MyExcel.Visible:=Visible;
        Result:=true;
      end
    else
      begin
        MessageBox(0,'Приложение MS Excel не установлено на этом компьютере','Ошибка',MB_OK+MB_ICONERROR);
        Result:=false;
      end;
  except
    Result:=false;
  end;
end;

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

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

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

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

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

function AddWorkBook(AutoRun:boolean=true):boolean;
begin
  if CheckExcelRun then
    begin
      MyExcel.WorkBooks.Add;
      Result:=true;
    end
  else
   if AutoRun then
     begin
       RunExcel;
       MyExcel.WorkBooks.Add;
       Result:=true;
     end
   else
     Result:=false;
end;

Второй вариант (более лаконичный):

function AddWorkBook(AutoRun: boolean = true): boolean;
begin
  Result := CheckExcelRun;
  if (not Result) and (AutoRun) then
  begin
    RunExcel;
    Result := CheckExcelRun;
  end;
  if Result then
    MyExcel.WorkBooks.Add;
end;

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

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

function GetAllWorkBooks:TStringList;
var i:integer;
begin
  try
    Result:=TStringList.Create;
    for i:=1 to MyExcel.WorkBooks.Count do
      Result.Add(MyExcel.WorkBooks.Item[i].FullName)
  except
    MessageBox(0,'Ошибка перечисления открытых книг','Ошибка',MB_OK+MB_ICONERROR);
  end;
end;

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

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

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

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

function SaveWorkBook(FileName:TFileName; WBIndex:integer):boolean;
begin
  try
    MyExcel.WorkBooks.Item[WBIndex].SaveAs(FileName);
    if MyExcel.WorkBooks.Item[WBIndex].Saved then
      Result:=true
    else
      Result:=false;
  except
    Result:=false;
  end;
end;

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

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

function StopExcel:boolean;
begin
  try
    if MyExcel.Visible then MyExcel.Visible:=false;
    MyExcel.Quit;
    MyExcel:=Unassigned;
    Result:=True;
  except
    Result:=false;
  end;
end;

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

Скачать исходник: Исходники —> Excel в Delphi
Следующая статья на тему работы с Excel в Delphi — «Excel в Delphi. Методы объекта WorkSheet (лист).»

Книжная полка

Автор: Юрий Магда
Название:Разработка приложений Microsoft Office 2007 в Delphi
Описание Описаны общие подходы к программированию приложений MS Office. Даны программные методы реализации функций MS Excel, MS Word, MS Access и MS Outlook в среде Delphi.
купить книгу delphi на ЛитРес

 

5 4 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
56 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
trackback
Социальная сеть для блоггеров sloger.net
27/08/2009 19:09

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

Рассматриваем основные функции для работы с Excel из Delphi…

webveter
28/08/2009 09:04

Если документ простой, но объемный — это будет медленно. А при большом количестве таких документов — жизнь пройдет мимо.
Вывести в Excel можно и проще.
 

Иван
Иван
30/09/2009 20:01

Очень хорошие статьи. Разобрался во всём с первого раза ))) Понравилась схема в mind42 )))
Автору остаётся сказать только спасибо!

Алекс
Алекс
10/01/2010 12:14

Все хорошо. Но если Excel не запущен — возникает исключение. Что с этим делать? function CheckExcelRun выбрасывает сообщение и все…Хотя по смыслу должно было вернуть False

Мария
21/03/2010 12:12

Насчет процедуры CheckExcelRun. Если программа запускается из под Delphi и Excel не запущен, то выскакивает ошибка: Операция GetActiveOleObject недоступна. Если програма запускается не из под Delphi то все в порядке. Наверное эту проблему имел ввиду Алекс. Как решение можно при отладке программы когда возникает исключение нажимать F9.

underchronos
underchronos
27/05/2010 21:19

Собственное говоря, а как при таком связывании отследить «сообщения» от Excel? Например, юзер «случайным» образом закрывает рабочую книгу, а программа все еще «хочет» работать с ней.

Динар
Динар
13/06/2010 17:13

произвожу экспорт в excel из delphi, все проходит как бы ровно. а неровность заключается в том что после экспорта остается жить процесс «excel.exe». пытался юзать (Excel.Quit; Excel:=Unassigned;) не хочет помогать. помогите плиз люди добрые

Виноградная Лиса
Виноградная Лиса
31/08/2010 12:48

Вобще очень крутая статья! Долго мучался очень я)
Не компилируется функция Save, «FileName:TFileName» вот этот кусок. Откуда это берется?

SeregaAltmer
31/08/2010 13:15

По сабжу пока не работал, но скорее всего либо перменная FileName уже объявлена, либо тип TFileName из модуля SysUtils, где-то переопределен.

Сообщение компилятора приведите без него только догадки строить остается.

SeregaAltmer
31/08/2010 14:05

Готовый модуль скачать не удалось… какието глюки на айфолдерс.
Написал по «инструкции» в посте. Все работает без проблем.

Shotkin
Shotkin
19/10/2010 09:27

Помогите Вопрос! Я использую шаблоны в своей программе! Можно ли из Дельфи изменть путь Save as..! Что бы при слхранений пользавателем путь сохранения был изменен? Спасибо

Kar
Kar
28/12/2010 20:14

А можно ли проверить, что не просто запущен Excel, а уже имеется открытая конкретная книга.
Например, хочу создать в Excel (усли уже создана, то открыть) книгу table.xls

Pups
Pups
29/12/2010 13:14

Эт конечно всё заебца, но в Д2009 не канает…

Denf
Denf
23/01/2011 09:45

begin
result:=CheckExcelRun;
if not result then
if AutoRun then
begin
RunExcel;
result:=CheckExcelRun;
end;
if result then MyExcel.WorkBooks.Add;
end;

Denf
Denf
23/01/2011 09:52

Или так :-)
begin
result:=CheckExcelRun;
if (not result) and (AutoRun) then
begin
RunExcel;
result:=CheckExcelRun;
end;
if result then MyExcel.WorkBooks.Add;
end;
 

Рома
Рома
01/02/2011 18:48

подскажите плиз,я недавно работаю с Delphi и у меня тут возникла проблемка..когда я использовал ваш код для проверки установлен Excel на компе или нет, у меня вылезла ошибка:
[Error] Unit5.pas(17): Identifier redeclared: ‘Rez’
объясните пожалуйста,в чем проблема?

Alex
Alex
02/02/2011 07:01

как подсчитать кол-во заполненых строк на листе?
в VBA так — Cells(Rows.Count, 1).End(xlUp).Row
а как в Delphi ее описать?

SmackMyBitchUp
SmackMyBitchUp
14/04/2011 18:31

function isExcelInstalled:boolean;
var
ClassID: TCLSID;
begin
Result := CLSIDFromProgID(PWideChar(WideString(ExcelApp)), ClassID) = S_OK;
end;

Понятнее и короче.

артем
артем
27/04/2011 21:53

Народ , а нельзя вбить какую нить формулу в excel и чтобы результат вычисления выводился в проге в Дельфи? просто я только разбираюсь в дельфи и не знаю как это через дельфи сделать..

igor`
igor`
24/05/2011 10:38

Это будет проще сделать в делфи, и на много облегчит программу.

kylt_lichnosti
kylt_lichnosti
04/07/2011 19:38

MyExcel.Application.EnableEvents:=DisableAlerts;
Если при поиске и замене ничего не найдено, то выдается сообщение — даже если DisableAlerts в False.
И почему DisableAlerts по умолчанию True — если сообщения лучше не показывать.
В моем случае помогло MyExcel.Application.DisplayAlerts := False.

kylt_lichnosti
kylt_lichnosti
04/07/2011 22:49

Хорошо, буду догадываться.
DisplayAlerts по идее отключает сообщения выдаваемые на экран. Но что тогда делает EnableEvents?

Katya
Katya
02/08/2011 17:23

В методе CheckExcelRun, если эксель закрыт возникает ошибка «Операция GetActiveOleObject недоступна.» а следом «invalid variant operation»

Katya
Katya
05/08/2011 11:38

И вообще работает не совсем корректно, т.к. если открыт другой документ excel то он сохраняет его, а не тот который формирует прога. Я только начинаю писать на делфи. Помогите исправить это

Ivan
Ivan
04/12/2011 17:29

var

  MsExcel : Variant;
  _Sheet : TStringList;
  i, x : integer;
  CCR_Ver, lLastRow, Name_Sheet, path : String;

begin

  // 1. Открываем форму CCR
  XLS_File.Execute();
  if XLS_File.FileName <> » then
    begin
      MsExcel := CreateOleObject(‘Excel.Application’);

      MsExcel.Workbooks.Open[XLS_File.FileName, 0, True];

      // видимость Excel
      MsExcel.Visible := False;
      // Закрытие открытой книги
      MsExcel.WorkBooks[MsExcel.WorkBooks.Count].Close(false);
      // Закрытие Excel
      MsExcel.quit;
      // Перераспределение памяти
      inherited;
      close; // Закрытие формы frm_CCR_mos
    end;

GS
GS
21/12/2011 21:14

Предлагаемый вариант конечно работоспособен, но у него есть важные недостатки:
— низкая производительность (попробуйте создать таблицу например размером 100 на 10000 ячеек со сложным форматированием)
— высокие требования к необходимой оперативной памяти (опять же см. пример с размером таблицы) 
— необходим установленный Excel (нет экселя, нет итогового файла. актуально, если подготовка отчетов делается на выделенном сервере, где лимитирован список установленного софта)

Лично я использую XLSReadWrite — вышеуказанных недостатков нет (таблица, указанную выше, у меня генерится за 0.4 секунды). Будет интересно — могу выложить реальный код примера

Руслан
Руслан
12/12/2014 16:37
Ответить на  Vlad

Влад, здравствуйте.
Я могу с Вами как либо связаться и задать пару вопросов?

GS
GS
23/12/2011 12:17

Упрощенный пример для Delphi 7 с использованием библиотеки XLSReadWrite: var   IntlXls:  TXLSReadWriteII2;   I, J: Integer; begin     // создаем объект     IntlXls := TXLSReadWriteII2.Create(nil);       // название книги       IntlXls.Sheets[0].Name := ‘Название моего отчета’;       // добавляем необходимое количество строк и колонок       IntlXls.Sheets[0].Rows.AddIfNone(0, 10000);       IntlXls.Sheets[0].Columns.AddIfNone(0, 100);       // добавляем и заносим ширины ячеек (значение в пикселях)       for I := 0 to 99 do         IntlXls.Sheets[0].Columns[I].PixelWidth := 150;       // заносим высоты строк (значение здесь не в пикселях, поэтому нужно корректировать)       for I := 0 to 9999 do         IntlXls.Sheets[0].Rows[I].Height := 20 * 14;       // настраиваем        for J := 0 to… Подробнее »

Вдовкин
Вдовкин
19/08/2014 22:00
Ответить на  GS

GS! Подскажите, где взять библиотеку XLSReadWrite?

GS
GS
23/12/2011 17:47

Буду только рад :)

Адик
Адик
01/04/2012 20:06

Ребят, вы даже представить себе не можете, насколько это полезная информация))) 
Спасибо! 

Павел
10/04/2012 18:39

Нужна помощь! 
1) В функции  CheckExcelInstall выдает результат false хотя exel установлен(2010)… 
2)Ругается на строчку MyExcel:=GetActiveOleObject(ExcelApp); так: «project project.exe raised exception class EOleSysError with message ‘недопустимая строка с указанием класса’.» 

Ivan
Ivan
17/04/2012 11:23

Доброго…
извиняюсь вторгнусь в беседу.

подскажите плиз как сделать следующее с помощью компонентов XLSReadWrite
1. открыть книгу
2. сделать поиск построчно в столбце например T при этом нужен поиск с частичным совпадением текста.
3. организовать поиск по многим книгам за 1 проход.

текущий пример как сделано сейчас прилагаю. Открытие книги и поиск в ней выполняются за 9 секунд в среднем в двух книгах за 18. хотелось бы уменьшить данное время на сколько это возможно.

заранее спасибо всем откликнувшимся.
 

Ivan
Ivan
17/04/2012 11:26

      // Создание Excel       ExcelApp := CreateOleObject(‘Excel.Application’);       // Отключаем реакцию Excel на события, чтобы ускорить вывод информации       ExcelApp.Application.EnableEvents := false;       //  Открываем Книгу (Workbook) в режиме только для чтения       ExcelApp.WorkBooks.Open(ExtractFilePath(ExtractFilePath(Application.ExeName)) + Book_Name, ReadOnly:=true);       WorkBookCount := ExcelApp.WorkBooks.Count;     end;   Sheet := ExcelApp.WorkBooks[WorkBookCount].Sheets[Sheet_Name];   // ThisWorkbook.Sheets(«Data»).Cells(1, 1).Select   Sheet.Cells[1, 1].select;   application.ProcessMessages;   // поиск адреса ячейки   vFind := Sheet.Range[»+Range_Find+»].Find( What:=Find_Site,                                         LookIn:=xlValues, // поиск в значениях                                         LookAt:=xlPart // частичное совпадение                                        // SearchDirection:=xlNext // направление вверх                                       ).Address;           XL_Col := Trim(Copy(vFind, 2));           XL_Col := Trim(Copy(XL_Col, 1, Pos(‘$’, XL_Col) -1) );… Подробнее »

Borodkin
Borodkin
17/09/2012 02:08

Вопрос поднимался выше, не ответили:
Не компилит функция Save, «FileName:TFileName», пишет:
1. если не определяем параметры функции SaveWorkSheet, пишет что не хватает актуальных параметров
2. ставлю параметры, (индекс), пишет что не соответствие типов String/Integer,

Akim
Akim
08/01/2013 02:47

Vlad огромное спасибо за статью
Просьба есть: нельзя ли перезалить модуль.. как тут уже писали он не скачивается по ссылке :(

Aine
Aine
12/02/2013 19:53

При объявлении переменой типа OleVariant, у самой переменой нету никаких методов/классов. После нажатия точки вылазит только (*, {, class, claasc и т.п. Так и должно быть?

Хотя сами методы компилятор видит. WorkBooks, Sheet, Range, Cells и т.п.

Carlos
Carlos
15/07/2013 23:16

кто-то может передать экземпляр сломанной ссылке

спасибо

novichok
novichok
21/08/2013 19:38

подскажите пож как сделать чтобы когда вбиваешь edit текс он выводился в excel и когда я водил опять он спускался на строчку ниже а не -заменял преждний

Айжана
Айжана
05/04/2015 21:54

procedure TForm1.EXSEL1Click(Sender: TObject); const StepRow=3; StepCol=1; var i:integer; WorkBk : _WorkBook; WorkSheet : _WorkSheet; begin ExcelApplication1.Connect; ExcelApplication1.WorkBooks.Add(xlWBatWorkSheet,0); WorkBk := ExcelApplication1.WorkBooks.Item[1]; WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet; WorkSheet.Name:=’ОТЧЕТ’; with WorkSheet do begin with PageSetup do begin Orientation:=2; CenterFooter:=’ОТЧЕТ’; FooterMargin; TopMargin:=25; LeftMargin:=0; BottomMargin:=100; RightMargin:=0; PageSetup.CenterHorizontally:=True; PageSetup.Zoom:=75; end; end; ExcelApplication1.Visible[0]:=true; with WorkSheet.Cells do begin Item[StepRow,StepCol].value:=’№’; Item[StepRow,StepCol].font.bold:=true; Item[StepRow,StepCol+1].value:=’Пэннін атауы’; Item[StepRow,StepCol+1].font.bold:=true; Item[StepRow,StepCol+2].value:=’Курс’; Item[StepRow,StepCol+2].font.bold:=true; Item[StepRow,StepCol+3].value:=’Болім’; Item[StepRow,StepCol+3].font.bold:=true; Item[StepRow,StepCol+4].value:=’Семестр’; Item[StepRow,StepCol+4].font.bold:=true; Item[StepRow,StepCol+5].value:=’Маман/к’; Item[StepRow,StepCol+5].font.bold:=true; Item[StepRow,StepCol+6].value:=’Лекция’; Item[StepRow,StepCol+6].font.bold:=true; Item[StepRow,StepCol+7].value:=’Практика’; Item[StepRow,StepCol+7].font.bold:=true; Item[StepRow,StepCol+8].value:=’Лабор.’; Item[StepRow,StepCol+8].font.bold:=true; Item[StepRow,StepCol+9].value:=’Рейтинг’; Item[StepRow,StepCol+9].font.bold:=true; Item[StepRow,StepCol+10].value:=’Курс. жумыс’; Item[StepRow,StepCol+10].font.bold:=true; Item[StepRow,StepCol+11].value:=’Консульт.’; Item[StepRow,StepCol+11].font.bold:=true; Item[StepRow,StepCol+12].value:=’Емтихан’; Item[StepRow,StepCol+12].font.bold:=true; Item[StepRow,StepCol+13].value:=’Онд.практика.’; Item[StepRow,StepCol+13].font.bold:=true; Item[StepRow,StepCol+14].value:=’СОЖ.’; Item[StepRow,StepCol+14].font.bold:=true; Item[StepRow,StepCol+15].value:=’Дипломдык жумыс’; Item[StepRow,StepCol+15].font.bold:=true; Item[StepRow,StepCol+16].value:=’Барлыгы:’; Item[StepRow,StepCol+16].font.bold:=true; with dm.QPredmed do begin First; i:=1; while not Eof do begin Item[StepRow+i,StepCol].value:=IntToStr(i); Item[StepRow+i,StepCol+1].value:=FieldByName(‘naz’).AsString;… Подробнее »