Jump to content

FastReport RichText / UniGUI


Alessandro

Recommended Posts

I have a serious problem with RichText.
- I´m use XE5 with FR 5.3.9.
- In the form, I put RichObject and the report put the RichText.
The problem occurs when I print, gives the error "window handle is invalid." sad.gif 
If I remove the report RichText, print normally.
I have uninstalled it and I downloaded again, but the error persists.
Would anyone tell me how to solve this?

 

Eu tenho um sério problema com RichText.
- Uso XE5 com FR 5.3.9.
- No form, eu coloquei RichObject e no relatório coloquei o RichText.
O problema ocorre quando eu vou imprimir, dá o erro: "identificador da janela é inválido."
Se eu remover do relatório o RichText, imprime normalmente.
Eu desinstalei e eu baixei de novo, mas o erro persiste.
Será que alguém me dizer como resolver isso?

 

Link to comment
Share on other sites

  • 2 months later...

I just spent a day trimming down a large datamodule that had about 20 reports on it and it finally came down to this one report with a TfrxRichView object on it. After I removed it, the datamodule would load.

 

Here's what's weird. I DO NOT share the datamodule object with other threads, so essentially... It is not multithreading the datamodule object<which contains the fast report report-objects>. I can create/destroy the datamodule many many times in a row. I tried a loop of create/destroy with the loop going up to 1000 times. I thought... "GREAT", but which I click on the button on the form to perform my action again, it locks up on the very first "create" call.

 

QUESTION: Why is is incompatible

What about the TfrxRichView is not compatible if considering that I AM NOT sharing the object with other threads?

 

QUESTION: Strange lock up

How do you explain that I can create/destroy/create/destroy 1000 times in a row perfectly, BUT then when I click the button to perform my buttonCick event again, it LOCKS UP on the very first create?

    pseudo-code

    MyButtonClickEvent(params)

    Var

       DM:TDataModule1;

    begin

    for x:=0 to 1000 do

         begin

         DM:=TDataModule1.Create(uniApplication);

         DM.Free;

         end;

    end;

 

QUESTION: Best practice for instantiating TDataModule

There are many ways to instantiate an object.

   1. DM:=TDataModule.Create(Nil)

   2. DM:=TDataModule.Create(uniApplication)

   3. DM:=TDataModule.Create(Self)

   4. Maybe others...

Which way is the proper way? I have been in the habit of using (NIL) and then freeing the object myself. 

 

QUESTION: TDataModule's AddDataModule overridden

I heard issues about the adding a datamodule logic in delphi that is "flawed" for a multithreaded environment. I see that YOU have taken over the "AddDataModule" function to do your own thing. DO you do that in order to prevent delphi flaws? Should I now assume that the adding of a datamodule will be proper<since you "unigui" takes over the adddatamodule logic> ?

 

Thanks

Davie

Link to comment
Share on other sites

Okay, point taken on question3, but what about question 1,2 and 4 ?

 

Keep in mind that UniGUII DOES take over the AddDataModule function to<i think> correct issues. I am working blind since I don't have source code, but that's what I think is going on. I see conflicting  Talking points on TDatamodule depending on if the module is to be SHARED amongst threads or not. I am NOT sharing the module with any other threads.

 

Davie

Link to comment
Share on other sites

Davie, you can get source code if you buy a license...

 

About DataModule vs. UniDataModule: even if you are not sharing your DataModule with other threads, what will happen when several concurrent users will access your web application at the same time ? Each user will create his own session... thus his own thread!  Your DataModule is not thread safe ; dbExpress, dbGo and FireDac components are not thread safe.  The only way to avoid thread conflicts is by using TUniDataModule, wich implements one DataModule for each concurrent session.

 

About RichView component: it is the same explanation for other standard VCL components (such as TfrxRichView) : they are not intended to succesfully run in multi-user and multi-session mode.  Your only safe solution is to use UniGui components.

 

And for your "strange behaviour", once again, it is impossible to reproduce and understand your problem without a test case.

Link to comment
Share on other sites

 

"dbExpress, dbGo and FireDac components are not thread safe."
 
Correcting
 

FireDAC is thread-safe if the following conditions are met:

  • A connection object and all objects associated with it (such as TFDQuery, TFDTransaction, and so on) are used by a single thread at each moment.
  • FDManager is activated before threads start, by setting FDManager.Active to True.

http://docwiki.embarcadero.com/RADStudio/XE6/en/Multithreading_%28FireDAC%29

 

I use .

Link to comment
Share on other sites

ZiGZig, First of all, I do NOT want to spend hundreds of dollars on a product with ZERO documentation and conflicting comments from various users when I don't even know it will be able to handle what I need. I've spent money on Fast reports cause it dd what I needed and it had docs and decent support. I paid money for SQLMemTables for the same reason. I paid for BusinessSkinForms for the same reason and on and on. Nowhere have I spent money on a beta product with no docs. Sorry. I would even be WILLING to spend the money, IF i knew it can do what I want EVEN though there is no docs. And that is what I am trying to do. Trying to see if it will do what I want.

 

Second, you mention to use TuniDataModule. I have no freaking idea of how to do that. I have no source I have no ability to SEE what unit contains that TuniDataModle in the first place. IN FACT, when I click "New" in the IDE, and pick the uniGUI for Delphi objects, then I pick the uniGUI Data Module, it auto creates a unit for me WITH TDATAMODULE. So even the UNIGUI system uses the TDATAMODULE object. Way too many inconsistiencies here. Try it yourself. Click on the IDE, "File" then "New" then open up the "Delphi Projects" tree, the click on the "uniGUI for Delphi" item and you will see 6 objects (Application Wizard, DataModule, Form, Frame, Inheritble Items, Mobile Application, Mobile Form). Pick the DataModule and you will see EXACTLY what I am talking about.

 

You said.....

About DataModule vs. UniDataModule: even if you are not sharing your DataModule with other threads, what will happen when several concurrent users will access your web application ? Your DataModule is not thread safe ; dbExpress, dbGo and FireDac components are not thread safe.  The only way to avoid thread conflicts is by using TUniDataModule, wich implements one DataModule for each concurrent session.

Wait a minute. I've been doing object programming for decades now. I must have a huge misunderstanding on how object programming works. I thought that if I created an object in one thread and that if that object is NEVER shared/referenced/used by another thread, that there is NO problem<even if the object is NOT thread safe> If I understand what you typed, it sounds like you are saying that TuniDataModule<which i can't find> will make several DataModule objectS, one for each session<thread>. And that this way is good. BUT yet in your first sentence you said that  "Even if you are NOT sharing your datamodule with other threads....." That makes me think that if I create an object and ONLY MY THREAD<session> will make use of it, that there will be problems. Did I understand you correctly? Because that makes no sense to me at all. Huh.

 

TfrxRichView IS NOT a visual component that you can plop onto a form and play with properties. It is part of the FAST REPORTS set of objects that you can plop onto a FastReport form. So, I don't know what you are talking about. It's an internal thing that fastreports creates to make a report do things. Just like the fastreports TfrxShapeView or TfrxMemoView. These are internal to FastReports so that the FastReport Designer can allow you to visually build a report layout. It's this TfrxRichView that I am "claiming" seems to cause a problem.... and wanted comments from people that used FastReports to hear what they had to say about the TfrxRichView thing.

 

Thanks for your fast reply.

 

Davie

Link to comment
Share on other sites

Marlon, you are totally right about FireDAC.

But I guess that with standard TDataModule, you won't get your first condition in a uniGUI context ("A connection object and all objects associated with it are used by a single thread at each moment.").  So if I understand, FireDAC+uniGUI will be thread-safe with TUniDataModule (edit : with uniGUI Data Module), but not with standard TDataModule (edit : with standard VCL Data Module)... and Davie want to use standard TDataModule.

Link to comment
Share on other sites

Davie,

 

 

When I write "TUniDataModule", it is all about making the difference with VCL DataModule. But you are right, it is basically the same class, I should write "uniGUI Data Module".

 

Anyway, look how uniGUI DM is instantiated :

function DataModule1: TDataModule1;begin  Result := TDataModule1(UniMainModule.GetModuleInstance(TDataModule1));end;
You can see that uniGUI does'nt call "Create" method, but "GetModuleInstance": you'll get one specific DataModule for each UniMainModule (thus for each session).

So yes : there will be several DataModule objects (one per session) if you use uniGUI DataModule.

 

Please notice that uniGUI doesn't use standard object constructor, as you want to do :

1. DM:=TDataModule.Create(Nil)

2. DM:=TDataModule.Create(uniApplication)

3. DM:=TDataModule.Create(Self)

 

 

With your way (TDataModule.Create), you take the risk to get only ONE object, whom parent is TUniApplication. Each session (each MainModule) would reference this same unique object. So yes: it is all about thread-safe and non thread-safe components.

If you want to avoid this issue, just use UniGUI DataModule, which instantiate new DataModule for each new session (UniMainModule.GetModuleInstance());

 

 

About TfrxRichView, it is exactly the same question; how is it coded? Are you sure it doesn't use global variables or methods? Are you sure that it is designed to run in a multi-thread context? Are you sure that when you create a new report, you won't share global variables or methods (not encapsulated into an object) with another one from another user?

If you want comments from people that used FastReport and uniGUI, just search in this forum: http://forums.unigui.com/index.php?app=core&module=search&do=search&fromMainBar=1 : you'll find a lot of topics saying that Rich Text gets problems with uniGUI.

 

UniGUI is still in (late) beta stage : you can try it, use it, comment it, test it, and even hate it. But it is intended to work as it works, with some technical limits (just like every framework): you need a DataModule ? Please use unIGUI DataModule, not standard VCL DataModule. You need Rich Text reports components? Please be advised that they are not stable with uniGUI, because of global variables and not thread-safe mode. You need full documentation? Please wait for version 1.0. You don't want to pay? Please don't hope to get full support.

 

Oh, by the way: I also have been doing object programming for decades now.

I don't know everything about uniGUI (I discover new things every day), I can make errors and misunderstand how it is internally coded. I use this forum to ask and answer about uniGUI, just like everyone here. But I don't use this forum to say that uniGUI should be written by FMSoft in a different way. I like some stuff, I dislike some technical limitations. UniGUI is what it is, no more, no less.

Link to comment
Share on other sites

Very nice. I understood almost all of what you say. Very clean. The one point I still don't get is this.

 

You say that If I use the " DM:=TDataModule1.Create(Nil) "  way of instantiating the object, you made it sound like <to me> that this object would/could be used by ALL THE SESSIONS?

 

That's the part that I absolutely don't get. I thought that if I create an object that it returns a pointer to a block of memory that is allocated for that specific instantiation. And that if I did another DM:=TDataModule1.Create(Nil), that that would allocate A NEW block of memory for for the second instantiation. And thus the pointer returned for the second creation line of logic would in fact be a different value. Thus I would have TWO pointers pointing to TWO different memory locations. 

 

If that is in fact the case<i believe it to be fact>, then if in my main module logic I have a line of code that says "DM:=TDataModule1.Create(NIL)"  and then store the object pointer into my TuniMainModule's public variables, then it would in fact be safe, since each session would have it's own copy of the TuniMainModule object. And that the DM object pointers that I am storing into the uniMainModule are in fact pointing at TWO DIFFERENT memory blocks. Each one distinct. Which would mean that any reference/reading/writing to the DM in session#1 would NOT interfere/modify/read/access with the DM that is stored in seesion two's uniMainModule.

 

I would bet serious money on that. Buy hey, I've been wrong before, so please spill the beans.

 

Thanks

Davie

Link to comment
Share on other sites

  • Administrators

I just spent a day trimming down a large datamodule that had about 20 reports on it and it finally came down to this one report with a TfrxRichView object on it. After I removed it, the datamodule would load.

 

TfrxRichView is not threadsafe. It doesn't play well in a threaded app.

 

Here's what's weird. I DO NOT share the datamodule object with other threads, so essentially... It is not multithreading the datamodule object. I can create/destroy the datamodule many many times in a row. I tried a loop of create/destroy with the loop going up to 1000 times. I thought... "GREAT", but which I click on the button on the form to perform my action again, it locks up on the very first "create" call.

 

All modules are forms in uniGUI are multi-threaded. Multi-threading is one issue here. uniGUI apps doesn't handle or implement Windows message system so any component relying on message loop will not work either.

 

QUESTION: Why is is incompatible

What about the TfrxRichView is not compatible if considering that I AM NOT sharing the object with other threads?

 

Simple. It is based on VCL TRichView  control which is not compatible with threaded apps. It is mentioned in VCL docs. FastReport has some threading issues in general. Problem here is that they rely on regular VCL controls to do certain jobs which can cause issues  in heavily threaded apps.

 

 

QUESTION: Strange lock up

How do you explain that I can create/destroy/create/destroy 1000 times in a row perfectly, BUT then when I click the button to perform my buttonCick event again, it LOCKS UP on the very first create?

    pseudo-code

    MyButtonClickEvent(params)

    Var

       DM:TDataModule1;

    begin

    for x:=0 to 1000 do

         begin

         DM:=TDataModule1.Create(uniApplication);

         DM.Free;

         end;

    end;

 

Where  this loop is running. Inside a uniGUI event?

 

QUESTION: Best practice for instantiating TDataModule

There are many ways to instantiate an object.

   1. DM:=TDataModule.Create(Nil)

   2. DM:=TDataModule.Create(uniApplication)

   3. DM:=TDataModule.Create(Self)

   4. Maybe others...

Which way is the proper way? I have been in the habit of using (NIL) and then freeing the object myself. 

 

Actually you don't need to instantiate a DataModule. Create a DataModule from uniGUI wizard, put your components and make the connections. uniGUI will do the rest for you.

 

QUESTION: TDataModule's AddDataModule overridden

I heard issues about the adding a datamodule logic in delphi that is "flawed" for a multithreaded environment. I see that YOU have taken over the "AddDataModule" function to do your own thing. DO you do that in order to prevent delphi flaws? Should I now assume that the adding of a datamodule will be proper ?

 

That's because each session should have its private copy of a DataModule to avoid confliction.

 

Thanks

Davie

Link to comment
Share on other sites

  • Administrators

Davie, you can get source code if you buy a license...

 

Yes. Let me remind that source code for core library is still not distributed.

 

; dbExpress, dbGo and FireDac components are not thread safe. 

 

Well, they are thread safe if you don't share same control with more one session which is automatically done if you put them on a MainModule, a UniForm,, a UniFrame or a DataModule which is created from uniGUI wizard.

Link to comment
Share on other sites

You say that If I use the " DM:=TDataModule1.Create(Nil) "  way of instantiating the object, you made it sound like <to me> that this object would/could be used by ALL THE SESSIONS?

Actually no, I don't say that.

I say that you TAKE THE RISK to.  I cannot say more: you didn't provide a test case, so it is impossible to figure where, when and how you create (and destroy) your datamodules ; it is also impossible to see if you have global variables into your datamodule unit (such as "var DataModule1:TDataModule1").  

For the rest, I think that Farshad made the point perfectly clear.

Link to comment
Share on other sites

 

Davie, you can get source code if you buy a license...

 

Yes. Let me remind that source code for core library is still not distributed.

 

; dbExpress, dbGo and FireDac components are not thread safe. 

 

Well, they are thread safe if you don't share same control with more one session which is automatically done if you put them on a MainModule, a UniForm,, a UniFrame or a DataModule which is created from uniGUI wizard.

 

You are right, obviously! 

Link to comment
Share on other sites

  • Administrators

ZiGZig, First of all, I do NOT want to spend hundreds of dollars on a product with ZERO documentation and conflicting comments from various users when I don't even know it will be able to handle what I need.

 

You are right on lack of docs. You are absolutely correct. Hopefully docs are the next in line.

I gave the #1 priority to stability and for other reasons documents were delayed. Not to mention that framework core has been redesigned almost three times and I waited until everything is settled down in term of internal design and stability.

 

BTW, what is your current version of uniGUI? Would you please mention your version and edition?

 

I've spent money on Fast reports cause it dd what I needed and it had docs and decent support. I paid money for SQLMemTables for the same reason. I paid for BusinessSkinForms for the same reason and on and on. Nowhere have I spent money on a beta product with no docs. Sorry. I would even be WILLING to spend the money, IF i knew it can do what I want EVEN though there is no docs. And that is what I am trying to do. Trying to see if it will do what I want.

 

Most of the stuff in uniGUI are straightforward and components are designed very close to VCL twins, so many users can go without detailed docs. Let me remind that product is still beta and we have announced that everyone with a valid license bought before 1st September 2015 is entitled to get version 1.00 regardless of the subscription status.

 

Second, you mention to use TuniDataModule. I have no freaking idea of how to do that. I have no source I have no ability to SEE what unit contains that TuniDataModle in the first place. IN FACT, when I click "New" in the IDE, and pick the uniGUI for Delphi objects, then I pick the uniGUI Data Module, it auto creates a unit for me WITH TDATAMODULE. So even the UNIGUI system uses the TDATAMODULE object. Way too many inconsistiencies here. Try it yourself. Click on the IDE, "File" then "New" then open up the "Delphi Projects" tree, the click on the "uniGUI for Delphi" item and you will see 6 objects (Application Wizard, DataModule, Form, Frame, Inheritble Items, Mobile Application, Mobile Form). Pick the DataModule and you will see EXACTLY what I am talking about.

 

You said.....

About DataModule vs. UniDataModule: even if you are not sharing your DataModule with other threads, what will happen when several concurrent users will access your web application ? Your DataModule is not thread safe ; dbExpress, dbGo and FireDac components are not thread safe.  The only way to avoid thread conflicts is by using TUniDataModule, wich implements one DataModule for each concurrent session.

Wait a minute. I've been doing object programming for decades now. I must have a huge misunderstanding on how object programming works. I thought that if I created an object in one thread and that if that object is NEVER shared/referenced/used by another thread, that there is NO problem If I understand what you typed, it sounds like you are saying that TuniDataModule will make several DataModule objectS, one for each session. And that this way is good. BUT yet in your first sentence you said that  "Even if you are NOT sharing your datamodule with other threads....." That makes me think that if I create an object and ONLY MY THREAD will make use of it, that there will be problems. Did I understand you correctly? Because that makes no sense to me at all. Huh.

 

By saying TUniDataModule ZigZig was referring to a DataModule which is created from uniGUI wizard not the one which you create from Delphi wizard. They look almost similar but they are not the same thing. One is handled by framework while the other is not.

 

TfrxRichView IS NOT a visual component that you can plop onto a form and play with properties. It is part of the FAST REPORTS set of objects that you can plop onto a FastReport form. So, I don't know what you are talking about. It's an internal thing that fastreports creates to make a report do things. Just like the fastreports TfrxShapeView or TfrxMemoView. These are internal to FastReports so that the FastReport Designer can allow you to visually build a report layout. It's this TfrxRichView that I am "claiming" seems to cause a problem.... and wanted comments from people that used FastReports to hear what they had to say about the TfrxRichView thing.

 

frxRichView internally creates TRichView which is not thread friendly.

 

Thanks for your fast reply.

 

Davie

Link to comment
Share on other sites

  • Administrators

Very nice. I understood almost all of what you say. Very clean. The one point I still don't get is this.

 

You say that If I use the " DM:=TDataModule1.Create(Nil) "  way of instantiating the object, you made it sound like that this object would/could be used by ALL THE SESSIONS?

 

You can create a DataModule yourself as long you manage it yourself and you create a separate copy in each session.

 

That's the part that I absolutely don't get. I thought that if I create an object that it returns a pointer to a block of memory that is allocated for that specific instantiation. And that if I did another DM:=TDataModule1.Create(Nil), that that would allocate A NEW block of memory for for the second instantiation. And thus the pointer returned for the second creation line of logic would in fact be a different value. Thus I would have TWO pointers pointing to TWO different memory locations. 

 

If that is in fact the case, then if in my main module logic I have a line of code that says "DM:=TDataModule1.Create(NIL)"  and then store the object pointer into my TuniMainModule's public variables, then it would in fact be safe, since each session would have it's own copy of the TuniMainModule object. And that the DM object pointers that I am storing into the uniMainModule are in fact pointing at TWO DIFFERENT memory blocks. Each one distinct. Which would mean that any reference/reading/writing to the DM in session#1 would NOT interfere/modify/read/access with the DM that is stored in seesion two's uniMainModule.

 

You are correct here. As long as you create a new copy of DataModule in UniMainModule OnCreate event and you keep its reference in a UniMainModule class variable it is safe.

 

Link to comment
Share on other sites

As far as TDatamodule goes, I created it with the uniGUI wizard that asks me if I want it to be "Application Datamodule" or "Free Datamodule".

 

When I pick "Free", it creates a module based on the "TDataModule". And that is defined from Delphi and not uniGUI. That was my point to ZigZig. IE: There is no TuniDataModule class. And from what I understand from you Farshad, is what I suspected, and that is it IS okay to create the datamodule object using   DM:=TDataModule1.Create(Nil)     as long as I keep it in one of my session specific objects<like mainmodule>. The way I understood Zig was that I couldn't do that and that that wouldn't be threadsafe. But I see in your post that you agree that it WILL be safe as long as I do what I mentioned. Okay, great.

 

Now, for the TfrxRichView object, I decided to put code in my fastreports source to debug it and found this in case anyone is interested...

 

 

constructor TfrxRichView.Create(AOwner: TComponent);
begin
inherited;
FRichEdit := TrxRichEdit.Create(nil);   //based on a VCL component
FRichEdit.Parent := frxParentForm;
SendMessage(frxParentForm.Handle, WM_CREATEHANDLE, frxInteger(FRichEdit), 0);
FRichEdit.AutoURLDetect := False;

{ make rich transparent }
SetWindowLong(FRichEdit.Handle, GWL_EXSTYLE,
GetWindowLong(FRichEdit.Handle, GWL_EXSTYLE) or WS_EX_TRANSPARENT);

FTempStream := TMemoryStream.Create;
FTempStream1 := TMemoryStream.Create;

FAllowExpressions := True;
FExpressionDelimiters := '[,]';
FGapX := 2;
FGapY := 1;
FWysiwyg := True;
FHasNextDataPart := False;
end;

 

Points of interest. The reference to frxParentForm IS A GLOBAL OBJECT :( Jerks. And to make matters worse, the use the SENDMESSAGE.

 

Now why are these interesting.... The frxParentForm being GLOBAL means that it can NEVER be threadsafe. Okay, bad news if several users at the same time. Now let's PRETEND that you can ENSURE that only ONE user has a session at any point in time, one would think that the frxParentForm would not cause an issue, BUT we have the SENDMESSAGE. Of course if the window handle doesn't have a message pump, then you are screwed and it will wait forever :(   Okay, so all that makes sense correct?

 

Here's what's weird. As I mentioned before, I can loop in my web form's buttonclick event over 1000 times to create the report object. I'm thinking.... if there is NO message pump, then shouldn't it lockup on the very first create? Bizzare how it works for awhile. Hmmmm Interesting.

 

Solution:

So, I guess I can't produce my nice reports that have a richview on it, the only other solution I saw here is that someone decided to spawn an EXE program and pass some parameters on the commandline that would create the report and save it to a known specified filename. Then the DLL code<when the spawn is done> would see the file that then send it to the end user.

 

Is that the best solution that you know of so far, given that I would like to use the RichEdit?

 

Thanks

 

Davie

P.S. I guess I'm too late to buy the product by Sept 1st. oh well.

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...