Шаг прислал Yegor A. Blackheel (blackheel@rlt.ru)
Вторым офисным приложением, автоматизацию которого мы рассмотрим, будет MS Excel. Это сделано в связи с тем, что автор получает много писем с просьбой рассказать именно об этой теме.
Предполагается, что читатель уже ознакомился с первыми тремя частями статьи и имеет представление об автоматизации, зачем она нужна, с чем её едят и т.д.
ВАЖНОЕ ЗАМЕЧАНИЕ! В связи с тем, что прогресс не стоит на месте, всё, что рассматривается ниже, подразумевает наличие Офиса ХР. А также из любви к новым технологиям пример разбирается для Visual C++ 7.0
Готовим проект так, как об этом рассказываалось в первой части статьи. Тип проекта - диалоговое окно, назовем его ExcelTest, с помощью редактора ресурсов добавим на форму диалога 2 кнопки - Test и Cancel . Кнопке Test зададим идентификатор IDС_TEST . Нечто подобное проделывалось ранее и не должно вызывать трудностей.
Поскольку ClassWizard'а в Visual C++ 7.0 больше не существует, зададим обработчик нажатия на эту кнопку простым даблкликом на кнопке. Сохраним название по умолчанию OnBnClickedTest() .
Также как и в случае Word'a нам необходима библиотека типов. На этот раз в качестве такой библиотеки выступит сам Excel. Структура объектов сервера автоматизации Excel схожа с о структуров объектов Word'a. Напомним, что самым верхним уровнем является Application (приложение), ниже которого лежат объекты и коллекции объектов. Объектами можно управлять напрямую, а из коллекции сначала нужно извлечь экземпляр, затем уже работать с ним. Но для нас все объекты и коллекции будут выглядеть просто как классы.
Для начала нам понадобятся такие объекты, как приложение, рабочая книга и лист рабочей книги (Application, Workbook и WorkSheet ), а также коллекции двух последних. Импортируем эти классы из библиотеки типов:
Вместо ClassWizard'a выбираем Project|Add class...
а в нём - создать класс MFC из библиотеки типов:
Теперь среда разработки сама показывает нам доступные библиотеки типов. Больше не надо рыскать по диску в поисках olb, tlb, dll и прочих exe :).
Как мы видим, в левом списке Interfaces находятся доступные интерфейсы объектов Excel'я. Перенесите _Application, _Workbook, Workbooks, Worksheets и _Worksheet в правый список Generated classes. По окончании нажмите кнопку Finish.
Add class From Typelib Wizard сгенерил классы по нашему запросу, и записал их в .h файлы с соответсвующими именами. Файлы эти были автоматически включены в проект.
Теперь все готово к старту.
Также как и Word, Excel заносит данные о себе и своих объектах в реестр, и к ним можно ображаться по символьным именам. Для Excel'а (любого, независимо от версии) это "Excel.Application".
В файл ExcelTest.cpp в функцию InitInstance() перед вызовом AfxEnableControlContainer(), т.е. в самом начале добавляем строки:
if(!AfxOleInit()) // Your addition starts here { AfxMessageBox("Could not initialize COM dll"); return FALSE; } // End of your addition
Это заставит проинициализироваться систему OLE. Если этого не сделать, то вызов CreateDispatch не сработает. В файл ExcelTestDlg.h добавим необходимые .h файлы. В класс CExcelTestDlg добавми переменную app типа CApplication, а в нашу функцию OnBnClickedTest добавляем следующий код:
if(!app.CreateDispatch("Excel.Application")) //запустить сервер { AfxMessageBox("Ошибка при старте Excel!");; return; } else app.put_Visible(TRUE); //и сделать его видимым CWorkbooks oBooks; CWorkbook oBook; COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); COleVariant covTrue((short)TRUE), covFalse((short)FALSE); //наша коллекция рабочих книг oBooks = app.get_Workbooks(); //добавить к ней новую книгу с шаблоном по умолчанию oBooks.Add(covOptional); //для Office XP //и получить её как экзепляр коллекции с номером 1 oBook = oBooks.get_Item(COleVariant(long(1))); //активизировать документ oBook.Activate();
Если все сделано правильно, то скомпилировав и запустив приложение вы откроете Excel с новой рабочей книгой с шаблоном по умолчанию, то есть "книга с тремя пустыми рабочими листами".
Добавми к нашей рабочей книге новый рабочий лист. Как мы уже знаем, в Excel'е все рабочие листы являются членами коллекции CWorksheets, которая, в свою очередь, принадлежит объекту CWorkbook - рабочая книга. Поэтому лист добавляем к коллекции листов вновь созданной рабочей книги:
CWorksheets oSheets; CWorksheet oSheet; //наша коллекция рабочих листов oSheets = oBook.get_Worksheets(); // добавить еще один рабочий лист (добавляем в начало, 1 лист, //тип "Рабочий лист") oSheet = oSheets.Add(covOptional,covOptional, COleVariant(long(1)), COleVariant(long(xlWorksheet))); //для Office XP //активизировать лист oSheet.put_Name("Мой рабочий лист"); oSheet.Activate();
Теперь мы можем оперировать с ячейками нашего рабочего листа. Для примера давайте занесём несколько числовых значений в ячейки, просуммируем их, поменяем стил границ и сольём четыре ячейки вместе. Для помещения текста в ячейки воспользуемся механизмом диапазонов, сходным с тем, что мы использовали в Word 'e. Добавьте классы CRange, CBorders, CBorder также, как описано в пункте 1.
// данные и формулы занесть.... CRange oRange = oSheet.get_Range(COleVariant("A1"),COleVariant("A1")); oRange.put_Formula(COleVariant("123")); oRange = oSheet.get_Range(COleVariant("A2"),COleVariant("A2")); oRange.put_Formula(COleVariant("456")); oRange = oSheet.get_Range(COleVariant("A3"),COleVariant("A3"));
//суммируем данные первых двух ячеек oRange.put_Formula(COleVariant("=SUM(A1:A2)")); // границы поменять на двойные.... oRange = oSheet.get_Range(COleVariant("A1"),COleVariant("A3")); CBorders oBorders; CBorder oBorder; oBorders = oRange.get_Borders(); oBorders.put_LineStyle(COleVariant(long(xlDouble))); // ... и внутрение убрать oRange = oSheet.get_Range(COleVariant("A1"),COleVariant("A3")); oBorders = oRange.get_Borders(); oBorder = oBorders.get_Item(long(xlInsideHorizontal)); oBorder.put_LineStyle(COleVariant(long(xlLineStyleNone))); // слить ячейки с С2 по D4 в одну oRange = oSheet.get_Range(COleVariant("C2"),COleVariant("D4")); oRange.Merge(covFalse); // включить перенос длинных строк и добавить длинный текст oRange.put_WrapText(covTrue); oRange.put_Formula(COleVariant("Длинный текст с переносом строк при достижении правой границы"));
Если все сделано правильно, то мы получим вот такой вид:
И, наконец, рассмотрим, как добавить еще один важный объект - диаграмму. Используя Add class From Typelib Wizard (как описано в пункте 1) добавим еще несколько классов, а именно: CCharts, CChart и CChartTitle.
Наша диаграмма будет располагаться на отдельном рабочем листе, содержать в качестве данных числа из ячеек A1:A3 , иметь тип "столбчатая", заголовок и называться "Моя диаграмма".
// диаграмма CCharts oCharts; CChart oChart; oCharts = oBook.get_Charts(); // добавить новую диаграмму oChart = oCharts.Add(covOptional,covOptional,COleVariant(long(1))); // установить тип диаграммы oChart.put_ChartType(long(xlBarClustered)); oChart.put_Name("Моя диаграмма"); // данные для диаграммы oRange = oSheet.get_Range(COleVariant("A1"),COleVariant("A3")); // связать диаграмму с данными oChart.SetSourceData(oRange,covOptional); //заголовок oChart.put_HasTitle(TRUE); CChartTitle oChartTitle; oChartTitle = oChart.get_ChartTitle(); oChartTitle.put_Caption("Заголовок моей диаграммы"); // активизировать лист с диаграммой oChart.Activate();
После того, как мы поработали с Excel-ем, его надо корректно закрыть. Корректно, это значит не просто вызвать метод Quit(), а еще и отпустить все интерфейсы. В сети постоянно задается вопрос "Я закрываю Excel, а он остается висеть в памяти, что делать?". У меня тоже этот вопрос появился, когдa я обнаружил, что закрывая программно Excel я вовсе не выгружаю его из памяти! Часовой поиск в инете, в частности, по форумам RSDN дал ключ. Итак, корректное закрытие всего и вся + выгрузка Excel-я:
oBook.Close(covFalse, covOptional, covOptional); oBook.ReleaseDispatch(); oBooks.Close(); oBooks.ReleaseDispatch(); app.Quit(); app.ReleaseDispatch();Замечание - пункт 4.6 включен в исходники в виде комментария. Поместите код в нужное место программы и снимите комментарий.
Здесь вы можете загрузить иллюстрирующий проект.
Скачать исходники - архив ZIP,~70 Кб
Шаг прислал Yegor A. Blackheel (blackheel@rlt.ru)