How to have an APP with Reports separate from the Main App

Hello everyone.
I need to separate a report from the main app.
For example:
→ parent.app with all functionalities
→ report.app only with the report
Explaining:
By separating the two, I can update only one or the other, without having to stop all the work on the client, when the update is for the layout, for example.
Is something like this possible?
return#=report.app(data)
if return false…

Only this report is customized for each client.

I would like a way to compile only this report, so that when I update the layout I don’t have to compile the entire system.

Which is what I do today.

thanks.

Just export the report to a TXA and create a seperate “Report app” that generates a DLL.

Each “Report app/dll” is unique to each customer with their own designs/layouts.

That way you dont have to recompile the whole main app every time.

Could you show me a simple example of how to do it?

Thank you in advance.

You know how to make a multi dll app?

If not have you seen this?

Some older ClarionLive webinars on the subject are on the webpage but the links are broken. You could ping John Hickey.

But there is still one video walking through the process: https://www.youtube.com/watch?v=t08o0KKniaY

Do you currently have your DCT and Files in a separate APP / DLL ?

That would be a first step to get working before making a DLL for Reports. You will find the DLL Tutor in the examples.


A simpler way I have seen done that avoids the DLL need is to make the Reports APP create another EXE. Then in your Main APP you RUN('MyReports.exe RunReport=Laudo_Todos_Relat'). Do not RUN( ,1) with the “,1” to wait, that will hang your main exe.

In the Reports EXE the Main procedure would a Source Procedure. It would check COMMAND() to see if there was a command line with a Report and run that then HALT. If no command it could call a Frame procedure. That would be very handy for your development and testing.

This way has some advantages that the reports are running in a separate process so your Main EXE is fully functional. You can always change the Reports.EXE to a DLL in the future.


To copy/move your current Report procedure to your new Reports APP my preferred way is to do a Selective Export to TXA, then Import TXA into the Reports APP. I have had problems with Import from APP so avoid it. This also leaves a little history of TXAs with procedures moved/copied.

Very interesting indeed!

I need to pass the ID number to the report, for example, report(tab:id).

Can I do RUN('MyReports.exe /ReportName(tab:id)')...?

I revised RUN(‘MyReports.exe /ReportName’)
to be a Name=Value pair

RUN('MyReports.exe RunReport=Laudo_Todos_Relat')

You would add more Command parameters like

RUN('MyReports.exe RunReport=Laudo_Todos_Relat  Tab4Report=' & tab:id )

So you could code something like this in your Reports.exe Main.

WhatReport STRING(32)
WhatTab    STRING(32)

   WhatReport=Command('RunReport')
   WhatTab   =Command('Tab4Report')
   CASE WhatReport
   OF 'Laudo_Todos_Relat' 
       Laudo_Todos_Relat(WhatTab) 
       HALT()
   ELSE
       Message('Unknown Report on commandline "' & CLIP(WhatReport) &'"' |
               '||Full Command()='& Command() ,'Report.exe Command() Check' )
       IF ~DevSystem THEN HALT().
   END

As you are working on getting Laudo_Todos_Relat() to work in a separate EXE you will probably find you are copying in other procedures and code it needs to work.

So now all of that is duplicated in both APPs. At some point you see too much duplicated and it makes sense to have DLLs so that code is in one place. Most of us have DLLs, it just works better.

You might still have the special Reports.EXE for each customer. But it shares common DLLs with you Main App that has all the shared code.

If you have a custom report for each client why don’t you look at Allerup Report Designer . I believe it’s currently Open Source and someone has ported it to C11, I remember a thread on this forum about it.
Allerup Report Designer lets you create custom reports without recompiling your app.

As for splitting your app into multi-dll, for some time I do it with all my apps except really small ones. Compiling one dll or exe takes significantly less time than compiling everything, even though I do clean solution and recompile everything before release anyway just to make sure there are no conflicts resulting from missing globals or changes in file structures. Recompiling everything takes some 30 mins, but that gives me some time to drink coffee and relax a bit.

I have a solution consisting of 8 dll’s and 6 exe’s ( exe’s mostly have just main menu procedure calling procedures in other dll’s as not all users need all functionalities ). Just note that if your exe is using a specific dll with a report it will lock it preventing you from replacing it as long as the exe is running anyway. However if you put your custom report in report.exe and not have it in parent.exe and other generic reports in genericrpt.dll. This way your both your report.exe and parent.exe can access reports in genericrpt.dll and your custom one will be in report.exe only.

Okay!

I learned how to generate the DLLs, but many cross-reference errors appear during compilation. So before going down that path, I have to remodel the system.

I only need one report to send out, I used the .exe option.
It worked very well, and I’m already going to distribute it to clients.

The remaining questions are:

Is it possible to:

→ pass a group in the command?
Example: run(myReport.exe report=Laudos group=Grp:qualquer)

→ have a return value from the generated .exe?
Example: loc:long=run(myReport.exe report=Laudos group=Grp:qualquer)

→ -> Generate a .exe without the main window

To solve these steps, I used .ini files to communicate between the two .exe files.
.
main app

report app

Thank you all very much.

Your code screen capture shows you are doing a RUN( ,1) i.e. 1 = waiting for the Run() to complete. I don’t like that because it will make your running EXE stop responding until the report completes …but…

That does allow your Report EXE to return a value using HALT(_Return_Long_) or I think SetExitCode(_Return_Long_).
Then in the main EXE after Run check the RunCode() function.

Run('myReport.exe report=Laudos group=Grp:qualquer', 1)
loc:RunReportExitCode = RunCode()
IF ErrorRCode() THEN
   Message('Run MyReport Failed '& Error()) ! Tell user Run failed, likely bad string; also EXE missing, corrupt, permissions
END

Copy your Main procedure to a procedure named Main_Frame … incase you want it later.

Delete Main, it should say Todo.
Add Main using the Source template.
In there add your code to process the Command().

Okay, another adjustment is that the run command() cannot have spaces between words.
For example, I need to pass the name of the PDF printer so that a certain type of report is printed. This is because Clarion’s PDF has errors when printing RTF text.
The most common printer name currently that does the job without causing problems is Microsoft Print to PDF, but in command() it only receives Microsoft.

I only treated this string with underscores in the forward process, and I remove them in the destination executable.

To pass or use strings with space’s in on window’s we have to encapsulate with double quotes.

eg long filenames “c:\program files\myappfolder\myapp.exe”
eg “c:\program files\myappfolder\myapp.exe” Parameter1=“My Long Parameter”

eg customizing_the_command_line_interface_clarioncl_exe_.htm [Clarion Community Help]

Okay!

I’ll try it and get back to you.
Thank you!

I’m learning a lot from this topic.
→ I already know how to break down an app and compile it into DLLs
→ I already know how to compile an exe
->> Without Main
->> Receiving data from another exe
->> Generating reports using existing code.

Thank you so much for everyone’s help!

As noted wrap the value in “quotes” e.g. Printer="Microsoft Print to PDF"

It is usually safe and best to always wrap in quotes if there is any chance of there being spaces, so all file names.

You mentioned using an INI file. You seem to have a lot of parameters so it may be easier to store them all in an INI and just pass the name of that. Then the only problem characters are 13 and to 10.