Home General
New Blog Posts: Merging Reports - Part 1 and Part 2

associating a datamodule with a report at runtime outside the main thread (within a DLL)

edited January 2015 in General
Hi all,

I have a thread in a dll which looks at a queue of report request
events and generates the reports (saved as xml files) on demand.

I create the report and the datamodule in the thread:


within the threads execute method I have:

:: // create the report
:: u_report := TppReport.Create( mainwnd );
:: u_report.Name := 'SERVER_EVENT_REPORT';
::
:: // ppBeginBackgroundPrinting; works sort of if uncommented, see below.
::
:: // create the datamodule
:: dmRBuilder := TdmRBuilder.Create( mainwnd );

(the datamodule contains a TADOConnection name conPC4)

Now when the thread wakes and sees a report in the queue it executes
thus (it looks up the type of report from the event and loads
templates using DADE):

:: with u_report.Template do
:: begin
:: with DatabaseSettings do
:: begin
:: DataPipeline := dmRBuilder.plItem;
:: Name := thisRbItemName;
:: NameField := 'ItemName';
:: TemplateField := 'Template';
::
:: end;
:: u_report.Template.LoadFromDatabase();
:: end;

:: u_report.DeviceType := dtXMLFile;
:: DateTimeToString(fileName, 'yyyy-mm-dd-hh-mm-ss-zzz', in_timeStamp);
:: u_report.TextFileName := format('%s%s.xml', [g_XMLFolder, filename]);
::
:: ServerLoadAutoSearch(in_reportEventID, in_itemID, u_report); // ERROR OCCURS HERE!!!!
::
:: u_report.Reset;
:: u_report.ShowPrintDialog := false;
:: u_report.ShowAutoSearchDialog := False;
:: u_report.ShowCancelDialog := false;
:: u_report.Print();

If I have uncommented ppBeginBackgroundPrinting, it runs nearly as
expected but slowly due to issues getting the DLL's synchronize list
to get pumped by the main application- a different issue i'm sorting
out myself currently.

HOWEVER if I am not "backgroundPrinting" then when it gets to
ServerLoadAutosearch I get an error about i need one ADO database
connection per thread.

looking at the stack trace the problem appears to be in
TdaConnectionList.BuildListRunTime (in daDB.pas) where we have:

:: if not(ppInMainThread) then
:: {do nothing, if this is a multi-threaded app then we really don't want it pulling
:: in other datamodules, because those are usually associated with individual
:: threads in a multi-threaded app. }
::
:: else
:: begin
::
:: for liIndex := 0 to Screen.DataModuleCount - 1 do
:: if (aOwner <> Screen.DataModules[liIndex]) then
:: BuildListFromOwner(Screen.DataModules[liIndex]);
::
:: for liIndex := 0 to Screen.FormCount - 1 do
:: if (aOwner <> Screen.Forms[liIndex]) then
:: BuildListFromOwner(Screen.Forms[liIndex]);
::
:: end;

I am not in the main thread, but if background printing is on then
ppInMainThread returns true and the BuildListFromOwner etc code
executes happily, finding the datamodule i have created and loading
its TADOconnection; ie it works.

BUT If not background printing ppInMainThread returns false, and so
the datamodule is not loaded, resulting in DADE not being able to find
its database connection the application error:

"...raise exception EDataErrorwith message
Tdasession.GetdatabaseForName: No TADOConnection object found for
specified name, conPC4'""

I'm sure I should be able to load the datamodule explicitly at the
start, eg something perhaps like
u_report...BuildListFromOwner(dmRBuilder);
but i can't see how to achieve this. I have it working for now by
commenting out the " if not(ppInMainThread) then" and "else" lines so
it forces the load but not happy leaving it this way.

Any help much appreciated.

Thanks,
Chris Were
(Reportbuilder 15.04 Enterprise, Delphi XE6)

Comments

  • edited January 2015
    Hi Chris,

    For a threaded architecture use a DataModule as a container for the Report +
    DB Connection. Then for each request, create an instance of the DataModule,
    generate the report, Free the DataModule.

    TDataModule
    TADOConnection
    TppReport

    The other thing to be aware of is that when a report template loads, the
    database name saved with the query is resolved by looking for the
    ADOConnection with the same name. The TppReport and TADOConnection must have
    the same owner/container which in the above is the DataModule.


    Best regards,

    -
    Nard Moseley
    Digital Metaphors
    www.digital-metaphors.com

    Best regards,

    Nard Moseley
    Digital Metaphors
    www.digital-metaphors.com
  • edited January 2015
    Thanks Nard that solved my problem, my previous (incorrect)
    interpretation had been that the datamodule and the report had to
    share the same owner, ie my ownership structure was something like:

    {BEGIN WRONG})
    TMyThreadComponent
    TppReport
    TDataModule
    TADOConnection
    {END WRONG}

    Sorted now though, thanks again.

    Chris
    On Tue, 13 Jan 2015 11:31:36 -0600, "Nard Moseley \(Digital
This discussion has been closed.