Если вы разрабатываете на C/C++ какое-либо ПО для операторов (администраторов) больниц, магазинов, сервисов проката гироскутеров, ремонта сотовых телефонов, то наверняка сталкивались с задачей создания отчетов, чтобы печатать их на принтере, ну или хотя бы в PDF. Существует множество сторонних пакетов для Embarcadero RAD Studio, которые позволяют это делать. Такие как FastReport, QuickReport, Crystal Reports и т.д. Но на мой взгляд все эти пакеты требуют глубокого вникания в свой механизм и интерфейс. Много времени уходит на их изучение, и что самое главное, код для генерирования даже самого простого отчета будет состоять из огромного числа строк. Когда я впервые столкнулся с задачей генерирования отчетов под C/C++, то начал с FastReport и понял, что этот инструмент мне абсолютно не нравится.
В этот самый момент в голову пришла замечательная мысль: как круто бы было взять простой EXCEL-файл, добавить в него статическую информацию и отформатировать под свои нужды. В программе останется только открыть этот файл, наполнить его динамическими данными и сохранить или отправить на печать! Это послужило отправной точкой к моему изучению OLE механизма работы с файлами MS Office из программ, разрабатываемых в Embarcadero RAD Studio.
По ходу деятельности удалось достаточно глубоко вникнуть в тему и сейчас могу с уверенностью сказать, что все те инструменты, которые предоставляет MS Office и OLE, перекрывают все мои потребности по созданию отчетов. Ну а раз так, то наверно и для других разработчиков это был бы самодостаточный, простой и удобный инструмент. Поэтому было принято решение написать DLL и упаковать в него перечень всех часто используемых функций MS Excel, с которыми приходится сталкиваться по ходу создания документов Excel. Это очень удобно, ведь для того, чтобы создать отчет, не нужно изучать кучу мануалов ил�� OLE. Всё, что требуется, это только подгрузить DLL и входящие в нее функции. Ну а с DLL работать умеют многие.
Вот сайт самого проекта.
На сайте проекта достаточно подробно расписан механизм работы с DLL, есть пример и описание всех функций.
DLL поставляется без заголовочного файла и без статической LIB библиотеки. Таким образом, подключать DLL необходимо динамически с использованием функции LoadLibrary. ZIP архив с библиотекой включает следующие файлы:
Заголовочный файл для примера использования библиотеки "LPDLL.h" содержит следующие объявления:
Файл «LPDLL.h» уже содержит всё необходимое для работы с DLL и ее функциями. Но вы также можете его отредактировать или взять только самое необходимое для вашего проекта.
Представим, что вы просто подключили к вашему проекту заголовочный файл «LPDLL.h».
Тогда в самом проекте вам просто необходимо:
1) Объявить переменную дескриптор вашего отчета:
2) Динамически загрузить DLL и все ее функции:
3) Далее в конструкции try-catch открыть заранее подготовленный шаблон отчета (файл MS Excel):
4) Добавить в отчет какие-нибудь данные:
…
5) Сохранить файл или напечатать:
6) Закрыть файл:
7) Выгрузить DLL библиотеку:
И всё! Абсолютно ничего лишнего! Есть лишь один нюанс, но и то, он проявляется только при отладке. В работающем приложении всё будет работать хорошо. Я имею ввиду ситуацию, при которой возникает какая-либо ошибка при работе с отчетом. Дело в том, что OLE механизм работы с документами предполагает только выброс исключений при возникновении ошибок. Вот почему при работе с функциями DLL необходимо применять конструкцию try-catch. В блоке catch необходимо без сохранения закрывать отчет:
При отладке, когда возникает ошибка, вы можете остановить работу программы. При этом процесс MS Excel останется запущенным, и закрыть его можно будет только через диспетчер задач. Поэтому при многократной отладке приложения может быть з��пущено несколько экземпляров процесса MS Excel в зависимости от того, как часто вы приостанавливали работу программы при возникновении ошибки, не дожидаясь выполнения кода в catch. За этим надо следить.
В релизе же, при возникновении ошибки обязательно сработает код, указанный в блоке catch, отчет закроется и процесс MS Excel будет завершен. Никаких висящих в системе процессов MS Excel наблюдаться не будет. Но все же стараются писать безошибочный код, поэтому надеюсь у вас не возникнет такая ситуация в работающем приложении.
Напоследок необходимо добавить:
Поэтому на будущее планируется упаковать весь функционал в класс и скрыть этот формат Variant внутри класса, чтобы наружу пользователю предоставлялись только общепринятые форматы C/C++. Не пробовал ни разу упаковывать классы в DLL, читал, что с этим обязательно возникнут трудности. Тем не менее будем разбираться! А пока, спасибо за внимание, буду очень рад, если эта статья и DLL кому-то помогут.
Ссылка на проект.
В этот самый момент в голову пришла замечательная мысль: как круто бы было взять простой EXCEL-файл, добавить в него статическую информацию и отформатировать под свои нужды. В программе останется только открыть этот файл, наполнить его динамическими данными и сохранить или отправить на печать! Это послужило отправной точкой к моему изучению OLE механизма работы с файлами MS Office из программ, разрабатываемых в Embarcadero RAD Studio.
По ходу деятельности удалось достаточно глубоко вникнуть в тему и сейчас могу с уверенностью сказать, что все те инструменты, которые предоставляет MS Office и OLE, перекрывают все мои потребности по созданию отчетов. Ну а раз так, то наверно и для других разработчиков это был бы самодостаточный, простой и удобный инструмент. Поэтому было принято решение написать DLL и упаковать в него перечень всех часто используемых функций MS Excel, с которыми приходится сталкиваться по ходу создания документов Excel. Это очень удобно, ведь для того, чтобы создать отчет, не нужно изучать кучу мануалов ил�� OLE. Всё, что требуется, это только подгрузить DLL и входящие в нее функции. Ну а с DLL работать умеют многие.
Вот сайт самого проекта.
На сайте проекта достаточно подробно расписан механизм работы с DLL, есть пример и описание всех функций.
DLL поставляется без заголовочного файла и без статической LIB библиотеки. Таким образом, подключать DLL необходимо динамически с использованием функции LoadLibrary. ZIP архив с библиотекой включает следующие файлы:
- "light_report.dll" — собственно сама DLL библиотека;
- "DLLTest.cpp" — пример использования библиотеки;
- "LPDLL.h" — заголовочный файл для примера использования библиотеки (это не заголовочный файл DLL);
- "Report.xlsx" — документ MS EXCEL для примера использования библиотеки.
Заголовочный файл для примера использования библиотеки "LPDLL.h" содержит следующие объявления:
- перечисления, которые используются в качестве аргументов функций DLL;
- типы функций DLL;
- экземпляры функций DLL;
- дескриптор загружаемой библиотеки DLL ("HINSTANCE DLL_Handle;");
- функция LoadLightReportDLL, которая динамически загружает DLL и все входящие в нее функции;
- функция FreeLightReportDLL, которая выгружает DLL.
Файл «LPDLL.h» уже содержит всё необходимое для работы с DLL и ее функциями. Но вы также можете его отредактировать или взять только самое необходимое для вашего проекта.
Представим, что вы просто подключили к вашему проекту заголовочный файл «LPDLL.h».
Тогда в самом проекте вам просто необходимо:
1) Объявить переменную дескриптор вашего отчета:
Variant report;
2) Динамически загрузить DLL и все ее функции:
if(!LoadLightReportDLL("C:\\LightReport\\light_report.dll"))return;
3) Далее в конструкции try-catch открыть заранее подготовленный шаблон отчета (файл MS Excel):
report=OpenReport("C:\\LightReport\\Report.xlsx",0);
4) Добавить в отчет какие-нибудь данные:
WriteCell(report, "Sheet1", 9, 1, "Hello world!");
…
5) Сохранить файл или напечатать:
Save(report); SaveAs(report, "C:\\LightReport\\Report copy.xlsx"); ExportToPDF(report, "C:\\LightReport\\Report.pdf", false); PrintOut(report);
6) Закрыть файл:
CloseReport(report);
7) Выгрузить DLL библиотеку:
FreeLightReportDLL();
И всё! Абсолютно ничего лишнего! Есть лишь один нюанс, но и то, он проявляется только при отладке. В работающем приложении всё будет работать хорошо. Я имею ввиду ситуацию, при которой возникает какая-либо ошибка при работе с отчетом. Дело в том, что OLE механизм работы с документами предполагает только выброс исключений при возникновении ошибок. Вот почему при работе с функциями DLL необходимо применять конструкцию try-catch. В блоке catch необходимо без сохранения закрывать отчет:
.catch(...){CloseReport(report);}
При отладке, когда возникает ошибка, вы можете остановить работу программы. При этом процесс MS Excel останется запущенным, и закрыть его можно будет только через диспетчер задач. Поэтому при многократной отладке приложения может быть з��пущено несколько экземпляров процесса MS Excel в зависимости от того, как часто вы приостанавливали работу программы при возникновении ошибки, не дожидаясь выполнения кода в catch. За этим надо следить.
В релизе же, при возникновении ошибки обязательно сработает код, указанный в блоке catch, отчет закроется и процесс MS Excel будет завершен. Никаких висящих в системе процессов MS Excel наблюдаться не будет. Но все же стараются писать безошибочный код, поэтому надеюсь у вас не возникнет такая ситуация в работающем приложении.
Напоследок необходимо добавить:
- В DLL применяются платформонезависимые типы данных, такие как, unsigned short, unsigned long и char. Это понятно почему.
- Пример написан в среде Embarcadero Builder C++ 10. Соответственно, весь код соотносится с этой средой и, возможно, вам необходимо будет внести кое-какие изменения в код примера, чтобы всё заработало в вашем окружении.
- Узким местом этой DLL является использование дескритора файла отчета в формате Variant. Это довольно специфический формат и, подозреваю, что могут возникнуть трудности с использованием библиотеки вне Embarcadero RAD Studio. Честно, не проверял.
Поэтому на будущее планируется упаковать весь функционал в класс и скрыть этот формат Variant внутри класса, чтобы наружу пользователю предоставлялись только общепринятые форматы C/C++. Не пробовал ни разу упаковывать классы в DLL, читал, что с этим обязательно возникнут трудности. Тем не менее будем разбираться! А пока, спасибо за внимание, буду очень рад, если эта статья и DLL кому-то помогут.
Ссылка на проект.