Шаг 397 - Автоматизация приложений MS Office. Часть 4.

Шаг прислал Yegor A. Blackheel (blackheel@rlt.ru)

И снова начало.

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

Предполагается, что читатель уже ознакомился с первыми тремя частями статьи и имеет представление об автоматизации, зачем она нужна, с чем её едят и т.д.

ВАЖНОЕ ЗАМЕЧАНИЕ! В связи с тем, что прогресс не стоит на месте, всё, что рассматривается ниже, подразумевает наличие Офиса ХР. А также из любви к новым технологиям пример разбирается для Visual C++ 7.0

4.1. Подготовка проекта.

Готовим проект так, как об этом рассказываалось в первой части статьи. Тип проекта - диалоговое окно, назовем его ExcelTest, с помощью редактора ресурсов добавим на форму диалога 2 кнопки - Test и Cancel . Кнопке Test зададим идентификатор IDС_TEST . Нечто подобное проделывалось ранее и не должно вызывать трудностей.

397_1.gif (33873 b)

Поскольку ClassWizard'а в Visual C++ 7.0 больше не существует, зададим обработчик нажатия на эту кнопку простым даблкликом на кнопке. Сохраним название по умолчанию OnBnClickedTest() .

Также как и в случае Word'a нам необходима библиотека типов. На этот раз в качестве такой библиотеки выступит сам Excel. Структура объектов сервера автоматизации Excel схожа с о структуров объектов Word'a. Напомним, что самым верхним уровнем является Application (приложение), ниже которого лежат объекты и коллекции объектов. Объектами можно управлять напрямую, а из коллекции сначала нужно извлечь экземпляр, затем уже работать с ним. Но для нас все объекты и коллекции будут выглядеть просто как классы.

Для начала нам понадобятся такие объекты, как приложение, рабочая книга и лист рабочей книги (Application, Workbook и WorkSheet ), а также коллекции двух последних. Импортируем эти классы из библиотеки типов:

Вместо ClassWizard'a выбираем Project|Add class...

397_2.gif (3431 b)

а в нём - создать класс MFC из библиотеки типов:

397_3.gif (18323 b)

Теперь среда разработки сама показывает нам доступные библиотеки типов. Больше не надо рыскать по диску в поисках olb, tlb, dll и прочих exe :).

397_4.gif (16368 b)

Как мы видим, в левом списке Interfaces находятся доступные интерфейсы объектов Excel'я. Перенесите _Application, _Workbook, Workbooks, Worksheets и _Worksheet в правый список Generated classes. По окончании нажмите кнопку Finish.

Add class From Typelib Wizard сгенерил классы по нашему запросу, и записал их в .h файлы с соответсвующими именами. Файлы эти были автоматически включены в проект.

Теперь все готово к старту.

4.2. Пуск!

Также как и 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 с новой рабочей книгой с шаблоном по умолчанию, то есть "книга с тремя пустыми рабочими листами".

4.3. Падают, падают, падают листья...

Добавми к нашей рабочей книге новый рабочий лист. Как мы уже знаем, в 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();

4.4. Детки в клетке.

Теперь мы можем оперировать с ячейками нашего рабочего листа. Для примера давайте занесём несколько числовых значений в ячейки, просуммируем их, поменяем стил границ и сольём четыре ячейки вместе. Для помещения текста в ячейки воспользуемся механизмом диапазонов, сходным с тем, что мы использовали в 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("Длинный текст с переносом строк при достижении правой границы"));

Если все сделано правильно, то мы получим вот такой вид:

397_5.gif (17509 b)

4.5. ...чертили чёрными чернилами чертёж....

И, наконец, рассмотрим, как добавить еще один важный объект - диаграмму. Используя 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();

397_6.gif (64453 b)

4.6. Чисто там, где убирают!

После того, как мы поработали с 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)


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 08.04.2008