Если вы разрабатываете на 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 архив с библиотекой включает следующие файлы:

  • "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 наблюдаться не будет. Но все же стараются писать безошибочный код, поэтому надеюсь у вас не возникнет такая ситуация в работающем приложении.

Напоследок необходимо добавить:

  1. В DLL применяются платформонезависимые типы данных, такие как, unsigned short, unsigned long и char. Это понятно почему.
  2. Пример написан в среде Embarcadero Builder C++ 10. Соответственно, весь код соотносится с этой средой и, возможно, вам необходимо будет внести кое-какие изменения в код примера, чтобы всё заработало в вашем окружении.
  3. Узким местом этой DLL является использование дескритора файла отчета в формате Variant. Это довольно специфический формат и, подозреваю, что могут возникнуть трудности с использованием библиотеки вне Embarcadero RAD Studio. Честно, не проверял.

Поэтому на будущее планируется упаковать весь функционал в класс и скрыть этот формат Variant внутри класса, чтобы наружу пользователю предоставлялись только общепринятые форматы C/C++. Не пробовал ни разу упаковывать классы в DLL, читал, что с этим обязательно возникнут трудности. Тем не менее будем разбираться! А пока, спасибо за внимание, буду очень рад, если эта статья и DLL кому-то помогут.

Ссылка на проект.