Sometimes we need create many reports in multiple threads simultaneously. This may be the development of a web service, or output information from an existing multithreaded application in a certain needed document format (PDF as example).
I noted that FastReport VCL library is a better solution for document generation in multiple formats. This component library is easy to use and has a convenient report designer which allows you to easily connect to different data sources, among which may be internal application data - arrays, sets of parameters, etc.
Traditional use of FastReport does not give any difficulties, but now we have to use this report generator in a multithreaded application. The output file format will be PDF.
TfrxReport class has a description of several properties which need to be set immediately after the creation of the object.
You need to remember that the object must work in a thread without the creation of dialogs, progress bars and other visual information.
Here is an example of creating and setting an object of class TfrxReport before the execution of the report:
// create report
FReport := TfrxReport.Crea te(nil);
// disable all messages
FReport.EngineO ptions.SilentMo de := True;
// enable safe work in threads
FReport.EngineO ptions.EnableTh readSafe := True;
// disable cache
FReport.EngineO ptions.UseFileC ache := false;
// disable progress bar
FReport.ShowPro gress := False;
Some reports have integrated dialog forms, and showing them should be banned for obvious reasons. We need to override an event handler TfrxReport.Engi ne.OnRunDialog by the procedure ShowReportDialo g for to any dialogs.
// handle all dialogs by ShowReportDialo g
FReport.Engine. OnRunDialog := ShowReportDialo g;
Our procedure will be executed instead of showing each report dialog. We can change the state of any control in a dialog, but we will leave this procedure empty.
procedure TTestThread.Sho wReportDialog(P age: TfrxDialogPage) ;
begin
// empty
end;
Then we create an object of TfrxPDFExport and disable the showing of dialog window and progress bar.
PDF := TfrxPDFExport.C reate(nil);
PDF.ShowDialog := False;
PDF.ShowProgres s := False;
All operations on the creation and exporting of report objects can be done in the constructor of the thread. The destructor of the thread should look like:
destructor TTestThread.Des troy;
begin
// destroy all created objects
PDF.Free;
FReport.Free;
inherited;
end;
Necessary objects are created and configured. Now you can load the report template from a file and run a report in the implementation of the main thread procedure Execute. There, it will also be exported to the desired format.
// load report template
FReport.LoadFro mFile(FFileName );
// set report variables
FReport.Variabl es['ThreadID'] := QuotedStr(FId);
// run report
if FReport.Prepare Report then
begin
// save result in PDF
PDF.FileName := FOutPath + '\report_'+ FId +
'_' + FormatDateTime( 'YYYYYMMDDHHMMS S', Now) + '.pdf';
FReport.Export( PDF);
end;
Try building reports without using RichText objects because by so doing you can get an unstable application.
Do not forget to include ActiveX controls in uses module, and add a call to CoInitialize (nil); in the procedure Execute before creating a report if the report is connected to ADO. Call to CoUninitialize at the end of the thread procedure.
// thread function
procedure TTestThread.Exe cute;
begin
// initialize COM library in current thread
CoInitialize(ni l);
try
// load report template from the file
FReport.LoadFro mFile(FFileName );
...
...
...
finally
// Uninitialize COM
CoUninitialize;
end;
end;
end;
You can see an attached example with the application which creates 10 reports in multiple threads and write many PDF files.
I noted that FastReport VCL library is a better solution for document generation in multiple formats. This component library is easy to use and has a convenient report designer which allows you to easily connect to different data sources, among which may be internal application data - arrays, sets of parameters, etc.
Traditional use of FastReport does not give any difficulties, but now we have to use this report generator in a multithreaded application. The output file format will be PDF.
TfrxReport class has a description of several properties which need to be set immediately after the creation of the object.
You need to remember that the object must work in a thread without the creation of dialogs, progress bars and other visual information.
Here is an example of creating and setting an object of class TfrxReport before the execution of the report:
// create report
FReport := TfrxReport.Crea te(nil);
// disable all messages
FReport.EngineO ptions.SilentMo de := True;
// enable safe work in threads
FReport.EngineO ptions.EnableTh readSafe := True;
// disable cache
FReport.EngineO ptions.UseFileC ache := false;
// disable progress bar
FReport.ShowPro gress := False;
Some reports have integrated dialog forms, and showing them should be banned for obvious reasons. We need to override an event handler TfrxReport.Engi ne.OnRunDialog by the procedure ShowReportDialo g for to any dialogs.
// handle all dialogs by ShowReportDialo g
FReport.Engine. OnRunDialog := ShowReportDialo g;
Our procedure will be executed instead of showing each report dialog. We can change the state of any control in a dialog, but we will leave this procedure empty.
procedure TTestThread.Sho wReportDialog(P age: TfrxDialogPage) ;
begin
// empty
end;
Then we create an object of TfrxPDFExport and disable the showing of dialog window and progress bar.
PDF := TfrxPDFExport.C reate(nil);
PDF.ShowDialog := False;
PDF.ShowProgres s := False;
All operations on the creation and exporting of report objects can be done in the constructor of the thread. The destructor of the thread should look like:
destructor TTestThread.Des troy;
begin
// destroy all created objects
PDF.Free;
FReport.Free;
inherited;
end;
Necessary objects are created and configured. Now you can load the report template from a file and run a report in the implementation of the main thread procedure Execute. There, it will also be exported to the desired format.
// load report template
FReport.LoadFro mFile(FFileName );
// set report variables
FReport.Variabl es['ThreadID'] := QuotedStr(FId);
// run report
if FReport.Prepare Report then
begin
// save result in PDF
PDF.FileName := FOutPath + '\report_'+ FId +
'_' + FormatDateTime( 'YYYYYMMDDHHMMS S', Now) + '.pdf';
FReport.Export( PDF);
end;
Try building reports without using RichText objects because by so doing you can get an unstable application.
Do not forget to include ActiveX controls in uses module, and add a call to CoInitialize (nil); in the procedure Execute before creating a report if the report is connected to ADO. Call to CoUninitialize at the end of the thread procedure.
// thread function
procedure TTestThread.Exe cute;
begin
// initialize COM library in current thread
CoInitialize(ni l);
try
// load report template from the file
FReport.LoadFro mFile(FFileName );
...
...
...
finally
// Uninitialize COM
CoUninitialize;
end;
end;
end;
You can see an attached example with the application which creates 10 reports in multiple threads and write many PDF files.
Comment