1. Steen Jakobsen
  2. PowerBuilder
  3. Thursday, 18 February 2021 06:54 AM UTC

This trick from  Chris Pollach @Appeon is FANTASTIC!!

add a pb.ini to the folder of your executable

 

PB.ini

------------------

[DataStore Behavior]

UseHwnd=no

----------------------



I have done that for a while (2 months) now and it fixes ALL RANDOM CRASH happening.

And there are zero side effects.

I'm just so happy I cannot express. The random crashes constantly happened and we have been chasing our own code for moths. The "USER OBJECTS" in the windows task manager went from several hundreds to just 4 and now everything works perfect and maybe a bit faster.


*** THANK YOU Chris for this tip :-) ***

Everyone should know!!


//Steen
 
Steen Jakobsen Accepted Answer Pending Moderation
  1. Sunday, 4 April 2021 08:39 AM UTC
  2. PowerBuilder
  3. # 1

Hi,

 

just an update on this. 

 

EVERYTHING is working PERFECT .. no side effect.  I just thing this is wonderful - it made a world of difference for stability when using many large batchjobs in separate instances of the same PB app. :-)

 

//Steen

Comment
There are no comments made yet.
Benjamin Gaesslein Accepted Answer Pending Moderation
  1. Wednesday, 24 March 2021 10:42 AM UTC
  2. PowerBuilder
  3. # 2

I've found a side effect after all:

when UseHwnd=no is set, using Print(true,true) on a datastore (or the pfc_print event, which also does Print(true,true)) will not show the printer selection box and instead shows the print dialog for the current default printer. Now the help does state "When working with DataStores, the showprintdialog argument must always be set to false." So I guess UseHwnd=yes enables a datastore to be printed like a datawindow.

Comment
  1. Steen Jakobsen
  2. Thursday, 25 March 2021 06:49 AM UTC
I guess you are right. But the performance and stability for using this trick is x1000 the work to print from a DW control!

It has made a "night vs. day" change in the stability and performance and I have zero side effects. :-)



//Steen
  1. Helpful
  1. Benjamin Gaesslein
  2. Thursday, 25 March 2021 11:37 AM UTC
The performance increase is indeed astonishing. But we're using many datastores to print reports from NWOs, there's no chance to convert all these pfc_print() calls to something else.

BUT I've managed to work around this and recreate the proper behaviour by overriding the pfc_print event of pfc_n_ds. My first idea was to create a datawindow, use datasharing and simply print the DW instead. But this doesn't work for several reasons; first of all a DW has to be created using OpenUserObject, which is not directly possible from an NVO. And datasharing will not share nested reports automatically.

What NVOs *can* do is open windows, which in turn *can* create DWs via OpenUserObject. So I've worked around the first problem by creating dummy window and Datawindow objects. Now pfc_print opens an invisible window, which opens the dummy DW, sets the dataobject to the one the DS uses and prints the DW via print(true,true). In the printstart event of the dummy DW, I do a printcancel() so it won't actually print. But all the settings from the print dialog are then written to the DW. Then I get the print attributes via describe, set them to the DS via modify and print the actual DS using print(false,false). Since the dummy DW print function always returns -1 in this case, the DW also has to have a boolean flag to track wether the printstart event actually fired or if the user pressed the cancel button on the print dialog.

Another caveat is that selecting a range of pages to print doesn't work this way, because the dummy DW is empty. This can also be worked around by reading the PageCount() from the DS and doing insertrow(0) on the dummy DW in a loop until its PageCount() value is equal.



All in all it works well and is indistinguishable from the regular printing behaviour.
  1. Helpful
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Thursday, 25 February 2021 00:54 AM UTC
  2. PowerBuilder
  3. # 3

FWIW,

Interesting, when I use the 'correct' order of first doing a Reset() and then assigning the dataobject = "", it blows up in of_update_totals(), when exiting the app. I'm not using yield(). Here's the stack:

 

With the 'incorrect' order of doing first dataobject = "" and then the reset(), my stack looks like this and doesn't blow up, but the fact it's still passing through the function while the application seems to have been closed already, is a bit concerning: no local, instance or global variables, nothing shown on the stack, no way to inspect anything with QuickView. The app has obviously stopped already, but it still looks like I'm passing through this function:

Probably something we do wrong somewhere.

regards,

MiguelL

 

 

 

Comment
  1. Miguel Leeuwe
  2. Thursday, 25 February 2021 01:07 AM UTC
Small remak: I marked ll_rowcount = 0 in yellow, but to be honest that's irrelevant as it's not being assigned a value. The value is 0 though and that's why it's blowing up using [1] to access the row.
  1. Helpful
  1. Chris Pollach @Appeon
  2. Thursday, 25 February 2021 01:08 AM UTC
No, you should only be doing this on the DC / DS's "Destructor" event. ;-)
  1. Helpful
  1. Miguel Leeuwe
  2. Thursday, 25 February 2021 01:11 AM UTC
Yes that's were I was doing it ... Unless this is an answer on me commenting I would only use it in specific situations?

(like within of loop using datastores).
  1. Helpful
There are no comments made yet.
Miguel Leeuwe Accepted Answer Pending Moderation
  1. Wednesday, 24 February 2021 23:36 PM UTC
  2. PowerBuilder
  3. # 4

Hi Chris and Steen,

In the past, I followed Chris' advice on doing:

  • THIS.DataObject = ""       // Release DWO resources
  • THIS.Reset()                   // Release buffers

and that has been working great since July last year. But now Chris recommends something a little bit different, so I said let's try this:

  • THIS.Reset()                   // Release buffers
  • THIS.DataObject = ""       // Release DWO resources
  • Yield()                            // Let Garbage collection complete

All kind of weird things started to happen. I was getting blowups while running the application due to datawindows not having any rows, whereas before, they always had at least one row. It took me a while to remember this last change I did last Friday (weekend does a g_miguel.reset() ), and I undid the change: All problems went away.

So using the yield(), the execution order of events must have changed in some places in my code.

I'm not saying to not do the yield(), but just want to point out to test you code again after you do so. In my case there's too much bad coding going on, so I cannot use the yield().

Not too sure if it's the same doing things in this order:

 

  • THIS.DataObject = ""       // Release DWO resources
  • THIS.Reset()                   // Release buffers

or doing it in this order:

  • THIS.Reset()                   // Release buffers
  • THIS.DataObject = ""       // Release DWO resources

I think the last order would make more sense, so I'm going to try that out and will post if I have any problems.

Regards

MiguelL

 

 

 

Comment
  1. Miguel Leeuwe
  2. Thursday, 25 February 2021 01:03 AM UTC
YW Chris,

Yes, I'm taking the code out for now from our n_ds and u_dw. I'll only apply it when I run into trouble in specific situations. Maybe behavior of datawindows and datstores change and I'd rather not risk anything. For now our applications seem to do fine without it.

regards
  1. Helpful
  1. mike S
  2. Thursday, 25 February 2021 12:31 PM UTC
@chris, yes, you can assign an invalid dataobject name without it changing syntax of the dw/ds control. this is EXTREMELY USEFUL by the way and MUST work this way for runtime security and customizations.



I set a 'fake' dataobject name when I load in a datawindow's syntax from a database blob. I use the dataobject name to let my application 'know' that it was loaded from syntax rather than from a regular stored datawindow. I call the dw.create( datawindowsyntax), then set the dataobject name with an identifier so the application knows the identify of the syntax.



In security and customization processing, you need to know the object - for a datawindow that would be the name which is stored in dataobject. So if you are loading it dynamically from stored syntax in the database you still need a unique name to make the security and customization work.



  1. Helpful
  1. mike S
  2. Thursday, 25 February 2021 13:02 PM UTC
Actually, i take that back. I set the 'invalid' dataobject name FIRST, and THEN create the datawindow from syntax. If you set the dataobject name after the create, it will (or at least used to) destroy the datawindow. I should have read my code first rather than try to remember details of something i did years ago.....
  1. Helpful
There are no comments made yet.
John Fauss Accepted Answer Pending Moderation
  1. Thursday, 18 February 2021 19:51 PM UTC
  2. PowerBuilder
  3. # 5

A follow-on question for Chris or anyone else from Appeon:

What internal behavior does this pb.ini setting change (i.e., what does it do)? I'm happy for Steen and others who use this with no apparent side effects, but I would not conclude that there are no side effects just because I have not become aware of any side effects.

FWIW, I agree with Benjamin. Why isn't the disabling of this "feature" the standard, if it truly is harmless? Shouldn't the default setting or condition of PB be to keep applications from crashing randomly?

Comment
  1. Simone Olianti
  2. Wednesday, 24 February 2021 18:35 PM UTC
[DataStore Behavior]

UseHwnd=no

if i enable this on my pb.ini and starting my app from a compiled .EXE i'm getting trouble accessing external datastore migrated to snapdevelop web api and accessed via restclient. If i run it from the IDE it's working fine.

no idea why

  1. Helpful
  1. Chris Pollach @Appeon
  2. Wednesday, 24 February 2021 19:11 PM UTC
Hi Simone;

This does not make sense as the DS migrated to C# does not use this setting. It should not affect the C# code.

How is the DS in the PB App used (consumed) in conjunction with the C# Web API?

Regards ... Chris
  1. Helpful
  1. Simone Olianti
  2. Thursday, 25 February 2021 10:20 AM UTC
hi Chris here is how i am accessing the datastore



RESTClient inv_RestClient

inv_RestClient = CREATE RESTClient

inv_RESTClient.SetRequestHeader ("Content-Type", "application/json;charset=UTF-8")

inv_RestClient.Setrequestheader( "Accept-Encoding", "gzip")



datastore dss

dss = create datastore

dss.dataobject='dss_startreg_new_find'



ls_Request_Url = ls_url+"startregnewfind/Retrieve?codaz=" + ls_codaz

inv_RestClient.retrieve(dss, ls_Request_Url)



if dss.rowcount( ) > 0 then

messagebox("SUCCESS!", "")

else

messagebox("FAILED!", "")

end if

destroy dss

destroy inv_RestClient



if i put the pb.ini in the client folder where is the app .exe the retrieve always failing. If i remove it, it's working
  1. Helpful
There are no comments made yet.
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Thursday, 18 February 2021 15:24 PM UTC
  2. PowerBuilder
  3. # 6

Hi Steen ... you are most welcome - this is "awesome" news! Have you hugged a DataWindow today? ;-)

    Also everyone, here is what you should add to your DW Control and DS's on the Destructor event (hopefully you have one ancestor each of these classes) to help with the proper releasing of memory and GDI resources as well ...

  • THIS.Reset()                   // Release buffers
  • THIS.DataObject = ""       // Release DWO resources
  • Yield()                            // Let Garbage collection complete

I have been using this logic in my STD frameworks for EONS (App"eons" that is). A tip that I got from the PowerSoft engineers many moons ago!  :-)

PowerOn!

Regards ... Chris

Comment
  1. Steen Jakobsen
  2. Thursday, 18 February 2021 17:12 PM UTC
Great tips about the rest() etc.

Thanks again Chris. Your work and contribution is must appreciated.



//Steen
  1. Helpful
  1. Benjamin Gaesslein
  2. Thursday, 25 February 2021 07:33 AM UTC
Putting this in the destructors for n_ds and u_dw base objects causes my application to crash. I assume it's because some DS/DW further down the inheritance chain has some code in the destructor that clashes with this.
  1. Helpful
  1. Miguel Leeuwe
  2. Thursday, 25 February 2021 09:18 AM UTC
That's probably a very good point Benjamin.

regards
  1. Helpful
There are no comments made yet.
Benjamin Gaesslein Accepted Answer Pending Moderation
  1. Thursday, 18 February 2021 13:28 PM UTC
  2. PowerBuilder
  3. # 7

No downsides at all? Why is this neither documented nor switched off by default?

Comment
  1. Steen Jakobsen
  2. Thursday, 18 February 2021 17:11 PM UTC
No down side at all .. that is my experience over a 2 month production period. It simply makes your app rock solid specifically on Windows Server and having multiple instances running at the same time. I dont know why this is not implemented in PB. Someone mentioned that it was because you could potentially make your datastore visual and also some legacy dependency for EAServer. Why anyone would want to do that I do not get!!

If it was implemented in PB it could have saved med a TON of work.



But now thanks to Chris i'm hugging my datawindows and datastores all day long :-)

  1. Helpful
  1. mike S
  2. Thursday, 25 February 2021 20:28 PM UTC
  1. Helpful
  1. Steen Jakobsen
  2. Friday, 26 February 2021 06:19 AM UTC
Hi,



This has nothing to do with pb.ini

For what it is worth all works 100% perfect now for me and I'm just so overwhelmingly grateful for the tip from Chris Pollach @Appeon



// Steen



ps.

I do NOT use this:

THIS.Reset() // Release buffers

THIS.DataObject = "" // Release DWO resources

Yield() // Let Garbage collection complete



  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.