Построение отчетов в нескольких потоках на Delphi
Иногда требуется организовать многопоточное создание документов в каком-либо формате. Это может быть разработка веб-сервиса или вывод информации из уже существующего многопоточного приложения.
Для создания документов в нужном формате удобно использовать генератор отчетов FastReport VCL. Эта библиотека компонентов проста в освоении, имеет удобный дизайнер отчетов, позволяет легко подключаться к различным источникам данных, в числе которых могут быть и внутренние данные приложение — массивы, наборы параметров и пр. Традиционное использование FastReport, как правило, не вызывает никаких сложностей, но сейчас мы рассмотрим ситуацию применения этого генератора отчетов в многопоточном приложении. Выходным форматом файлов станет PDF.
Класс TfrxReport имеет в своем описании несколько свойств, которые необходимо настроить непосредственно после создания объекта отчета. Необходимо помнить, что объект должен работать в потоке без создания всевозможных диалоговых окон, прогресс бара и прочей визуальной информации. Рассмотрим пример создания и настройки объекта класса TfrxReport, перед его запуском в отчете:
1 2 3 4 5 6 7 8 9 10 | // Создаем объект FReport := TfrxReport.Create(nil); // Запрещаем вывод различных сообщений FReport.EngineOptions.SilentMode := True; // Опция многопоточности, проверяется в некоторых местах при построении отчета FReport.EngineOptions.EnableThreadSafe := True; // Использование файлового кеша при построении отчетов FReport.EngineOptions.UseFileCache := false; // Запрещаем показ прогресс бара FReport.ShowProgress := False; |
Также необходимо помнить о том, что некоторые отчеты имеют интегрированные диалоговые формы, показ которых должен быть запрещен по понятным причинам. Для перехвата форм переопределим обработчик события TfrxReport.Engine.OnRunDialog — присвоим ему процедуру ShowReportDialog.
1 2 | // При наличии в отчете диалогов, вместо них будет вызываться ShowReportDialog FReport.Engine.OnRunDialog := ShowReportDialog; |
Таким образом вместо показа каждой диалоговой формы отчета будет вызвана наша процедура. Там можно обратиться к контролам формы, изменить их значение, а можно все оставить как есть по умолчанию — в таком случае наша процедура будет пустой.
1 2 3 4 | procedure TTestThread.ShowReportDialog(Page: TfrxDialogPage); begin // пусто end; |
Не забываем о настройках экспорта, нужно отключить показ всех диалогов и заранее установить все нужные нам параметры.
1 2 3 | PDF := TfrxPDFExport.Create(nil); PDF.ShowDialog := False; PDF.ShowProgress := False; |
Все операции по созданию объектов отчета и экспорта можно сделать в конструкторе класса потока, соответственно деструктор потока должен содержать следующий код:
1 2 3 4 5 6 7 | destructor TTestThread.Destroy; begin // destroy all created objects PDF.Free; FReport.Free; inherited; end; |
Нужные объекты созданы и сконфигурированы. Теперь можно загрузить шаблон отчета из файла и запустить отчета на выполнение в главной процедуре потока Execute. Там же выполним экспорт в нужный формат.
1 2 3 4 5 6 7 8 9 10 11 12 | // загружаем шаблон отчета FReport.LoadFromFile(FFileName); // устанавливаем переменные отчета, если нужно FReport.Variables['ThreadID'] := QuotedStr(FId); // строим отчет if FReport.PrepareReport then begin // сохраняем результат в PDF PDF.FileName := FOutPath + '\report_'+ FId + '_' + FormatDateTime('YYYYYMMDDHHMMSS', Now) + '.pdf'; FReport.Export(PDF); end; |
Помните — если в отчете используется RichText, то в потоках могут быть проблемы. Старайтесь строить отчеты без использования RichText объектов. Если в отчетах используется подключение ADO, не забываем включить в uses модуль ActiveX и в процедуре Execute перед созданием отчета добавить вызов CoInitialize(nil). А после завершения работы с отчетов в потоке, соответственно нужно будет вызвать CoUninitialize.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // Процедура потока procedure TTestThread.Execute; begin // Инициализирует библиотеку COM в текущем потоке CoInitialize(nil); try // загружаем шаблон отчета из файла FReport.LoadFromFile(FFileName); ... ... ... finally // Uninitialize COM CoUninitialize; end; end; |
Copyright© 2010 Александр Федяшов, ведущий разработчик FastReports