View Issue Details

IDProjectCategoryView StatusLast Update
0037065PackagesLazReportpublic2020-05-30 09:38
ReporterZdravko Gabrovski Assigned ToJesus Reyes  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
PlatformWindowsOSWindows 10 
Fixed in Version2.1 (SVN) 
Summary0037065: Unable to use LazReport In DLL library under Windows 10
DescriptionI have a DLL library-plugin project that uses lazReport for preview and print various kind of reports.
My first try to use library under Windows 10 causes freeze of my host application, when it try to load library via standart SafeLoadLibrary function.
After deep debug i found, that LazReport LR_Prntr unit freeze under initialization section.
A line
Prn.Printer:=Printer;
Calls Windows API
CreateDCW
in
{fpcdir}\lazarus\components\printers\win32\winprinters.inc
Which hangs under Windows 10.

I think, that the problem is very similar as described here:
https://bugs.freepascal.org/view.php?id=26801
and here:
https://docs.microsoft.com/en-gb/windows/win32/dlls/dllmain?redirectedfrom=MSDN
***
The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code.
***

So, the fix I did is to remove then Prn initialization object code from initialization section, by creating a new "GlobalPrn" object, and replace a Prn with a function, that will init the Prn object on it first call:

in Interface section:
var
  GlobalPrn: TfrPrinter = nil;

function Prn : TfrPrinter;

in implementation section:


function Prn: TfrPrinter;
begin
if Assigned( GlobalPrn ) then
  Exit( GlobalPrn );

  GlobalPrn := TfrPrinter.Create;
  try
    GlobalPrn.Printer:=Printer;
  except
    on E: Exception do begin
      debugln('lazreport: unit lr_prntr: ',E.Message);
    end;
  end;
  Exit( GlobalPrn );
end;





Steps To ReproduceUnder Windows 10:

Create a simple library project MyLib.Dll, add a dependancy to LazReport Package.
Put "Lr_class" in your uses list.

Create a simpe host project with a one button, and put OnClickEvent

LibHandle := SafeLoadLibrary('MyLib.dll')

Run the host project, Press the button, it will freeze.

Additional InformationThe fix is in attached .diff file.
TagsNo tags attached.
Fixed in Revision63221
LazTarget2.2
Widgetset
Attached Files

Activities

Zdravko Gabrovski

2020-05-13 18:47

reporter  

lr_prntr.diff (1,256 bytes)   
Index: components/lazreport/source/lr_prntr.pas
===================================================================
--- components/lazreport/source/lr_prntr.pas	(revision 63135)
+++ components/lazreport/source/lr_prntr.pas	(working copy)
@@ -72,8 +72,10 @@
   end;
   
 var
-  Prn: TfrPrinter;
+  GlobalPrn: TfrPrinter = nil;
 
+function  Prn : TfrPrinter;
+
 const
   MAX_TYP_KNOWN = 118;
 
@@ -363,6 +365,25 @@
 //
 {$ENDIF}
 
+
+
+function Prn: TfrPrinter;
+begin
+if Assigned( GlobalPrn ) then
+  Exit( GlobalPrn );
+
+  GlobalPrn := TfrPrinter.Create;
+  try
+    GlobalPrn.Printer:=Printer;
+  except
+    on E: Exception do begin
+      debugln('lazreport: unit lr_prntr: ',E.Message);
+    end;
+  end;
+  Exit( GlobalPrn );
+end;
+
+
 {----------------------------------------------------------------------------}
 constructor TfrPrinter.Create;
 var
@@ -1245,16 +1266,9 @@
 {----------------------------------------------------------------------------}
 
 initialization
-  Prn := TfrPrinter.Create;
-  try
-    Prn.Printer:=Printer;
-  except
-    on E: Exception do begin
-      debugln('lazreport: unit lr_prntr: ',E.Message);
-    end;
-  end;
 
 finalization
-  Prn.Free;
+  if Assigned( GlobalPrn ) then
+    GlobalPrn.Free;
 
 end.
lr_prntr.diff (1,256 bytes)   

Jesus Reyes

2020-05-18 20:11

developer   ~0122907

please attach sample code that reproduce the problem.

Zdravko Gabrovski

2020-05-18 20:38

reporter   ~0122911

Last edited: 2020-05-18 20:39

View 2 revisions

PLese, here you are.

Zdravko Gabrovski

2020-05-18 20:42

reporter   ~0122912

Unzip it into some holder, there is two projects: Host.lpi and dllapp.lpi.
Just compile both of then and start host.exe (under Windows 10).
Then click the button.
Application will freeze. Note! You must have some printers installed! (Even Microsoft PDF Printer is enougth).

Then
Apply the patch, re-compile DLL Library with clean up and build and do the same.
Now the form will appear.
LazReportDllBug.7z (54,177 bytes)

Jesus Reyes

2020-05-19 22:39

developer   ~0122945

Last edited: 2020-05-19 22:43

View 2 revisions

Thanks for your sample project the unit uHostMain missed the unit dynlibs in the uses, I added it, unfortunately I cannot reproduce it here. Win10, FPC 3.0.4 32bits. default printer is CutePDF there are 8 printers more, all virtual printers.

Zdravko Gabrovski

2020-05-20 18:18

reporter   ~0122958

Dear Jesus, I will add missing unit. Please test it under Windows 10 64bit

Zdravko Gabrovski

2020-05-20 18:32

reporter   ~0122961

PLease, find attached new sample with exe and dll compiled inside.
The bug you can reproduce under Widows 10 64 bit.
LazReportDllBug-2.7z (1,556,651 bytes)

Zdravko Gabrovski

2020-05-20 22:38

reporter   ~0122966

With 64 bit fpc and lazarus from trunk.

Jesus Reyes

2020-05-21 14:16

developer   ~0122979

Thanks, I tried again with a lazarus/fpc 64 bit setup but still I cannot reproduce the problem, then I tried your included binaries (thanks for that) and those worked fine too, in the new installment there are only 3 printers, again all virtual....

Zdravko Gabrovski

2020-05-21 15:02

reporter   ~0122980

Last edited: 2020-05-21 15:03

View 2 revisions

ОК.
I would like to ask anyone who has the opportunity to test with the attached example.
I test it in tho different machines, I will check the exact build an Windows 10 version.
A also have test under Windows 2012 server - similar behavior, but it loads DLL after 3 minutes of "freeze".
With patch there is no problem.

I would like to point out that it would be good to remove the printer initialization from the Initialization section of the unit, like I did. for sure it is good practice in dll to use small pieces of code for local variables initialization, etc. in the initialization section , like describred here. https://docs.microsoft.com/en-gb/windows/win32/dlls/dllmain?redirectedfrom=MSDN

My patch only move the printer object creation from the initialization section to the first function call.
This only slightly changes the moment of the initial initialization of the object.

Hope, that some other will participate and test the issue.
Regards,
Zdravko

jamie philbrook

2020-05-21 15:39

reporter   ~0122982

May I make a suggestion?

  Its not good practice to load or attempt to load other DLL's while in the initial state of the currently DLL loading process..

  I think a better method would be to enclose all of the current initiation code within a Function of the printer unit and while in the initiation section of that unit, test for the Instance handle value, if it's not the Application handle Then do not call this enclosed function and have your code later make a initialization function call from the host app that initiates everything needed in the DLL, this includes the printer code, too.
  
  At least this way when the unit is used within an app it will still behave normally..

  hacking the code base to fit your needs in a DLL is just going to most likely generate issues with others in base applications.

 If that printer unit behaves like it does on my Windows 10, it goes out and queries the printers online and that takes time because for me it even does a network check of printers, that is the OS will do that too.

Zdravko Gabrovski

2020-05-23 09:42

reporter   ~0123009

Last edited: 2020-05-23 13:26

View 5 revisions

I am not agree that I am "hacking" the code, based on my needs. Did you check what is my patch doing???? It is very similar to your proposal!!

I am completely agree with you, that Its not good practice to load or attempt to load other DLL's while in the initial state of the currently DLL loading process.
I am not doing this in my code. I just have "uses LR_Class" in my codr, which uses "LR_Prntr" unit, which have this dll load in it initialization section.

In my patch I did excatly what you propose. I enclose current initiation code within a Function in the LR_PRNTR printer unit with the same name as global printer object and the same type.
I remove initiation code from initialization section.

So let me explain in details:

I the original code it is like this (unit lr_prntr):
There is a global variable:
var
  Prn: TfrPrinter;

and initialization code

initialization
  Prn := TfrPrinter.Create;
  try
    Prn.Printer:=Printer; <----------------- This line call Windows API Which load printer dll and freeze!!!
  except
    on E: Exception do begin
      debugln('lazreport: unit lr_prntr: ',E.Message);
    end;
  end;

My patch do the following:
1. Create New global object, called "GlobalPrn"
var
  GlobalPrn: TfrPrinter = nil;
2. Create a new function, called with the same name as previous global object, and the same type:
function Prn : TfrPrinter;
3. The function does the following: On the first call it will allocate a new "Global" prn object; otherways it will just return previously created:

function Prn: TfrPrinter;
begin
if Assigned( GlobalPrn ) then
  Exit( GlobalPrn );
// Here is the original code that I moved from initialization section
  GlobalPrn := TfrPrinter.Create;
  try
    GlobalPrn.Printer:=Printer;
  except
    on E: Exception do begin
      debugln('lazreport: unit lr_prntr: ',E.Message);
    end;
  end;
  Exit( GlobalPrn );
end;
4. Just in case, set a new initialization section

initialization
  GlobalPrn := nil;

I am doing exactly what you propose! The only difference is that there will be no need to call some special initialization function from my host app code, because it will happens automatically when LAzReport Need a Prn object for first time, and this time for sure is not during library initiaziation phase!

Regards,
Zdravko

emthreex

2020-05-23 15:22

reporter   ~0123012

I can confirm the bug. I am running the sample on the following OS:
OS Name: Microsoft Windows 10 Pro
OS Version: 10.0.18362 N/A Build 18362
I used the provided executable from LazReportDllBug-2.7z
Here is a video, showing the problem. When I removed my printer, everything worked.
https://youtu.be/r14FT1z9g1g

jamie philbrook

2020-05-23 20:31

reporter   ~0123015

I've seen lags using the lazprinters unit even in host apps.. but I don't get a long delay like you are speaking of and it seems random...
maybe it would be a better place to initiate the printers when needing them..

 But i comment was to have the code detour if it detects that it is in a DLL and not a HOST app.

 Apparently when loading all the DLL's in the system things get slow and if it does detect a printer than it may take a little longer to setup..

 The issue I see here is this printer unit needs to be initated at all times because I make references to it before I even start a print job and of course these references must be valid..

Zdravko Gabrovski

2020-05-23 22:07

reporter   ~0123024

@Jamie, I am talking about LR_Prntr, not for the printer unit.
It could be some check if it is in host app or in DLL, but, for me, it is not good to have printer initialization even in host app, thats the reason that I move initialization when LazReport need object for first time, which for sure will not be in initialization phase.
@emthreex thanks for your participation and tests you did! I have exactly the same behavior like in your movie.
Under Windows 2012 server I check it freeze for 5 minutes and after that display DLL form, which is not acceptable for user to wait 5 minutes.

Jesus Reyes

2020-05-25 19:04

developer   ~0123060

Applied the patch with the only change of moving the GlobalPrn variable to the implementation section as the change from variable to function forprn should be enough for causing no further problems or else we have to think in another solution. Thanks.

Zdravko Gabrovski

2020-05-25 22:01

reporter   ~0123063

Thanks, Jesus!

Zdravko Gabrovski

2020-05-30 09:38

reporter   ~0123140

Thanks!

Issue History

Date Modified Username Field Change
2020-05-13 18:47 Zdravko Gabrovski New Issue
2020-05-13 18:47 Zdravko Gabrovski File Added: lr_prntr.diff
2020-05-15 03:27 Jesus Reyes Assigned To => Jesus Reyes
2020-05-15 03:27 Jesus Reyes Status new => assigned
2020-05-18 20:11 Jesus Reyes Status assigned => feedback
2020-05-18 20:11 Jesus Reyes LazTarget => -
2020-05-18 20:11 Jesus Reyes Note Added: 0122907
2020-05-18 20:38 Zdravko Gabrovski Note Added: 0122911
2020-05-18 20:38 Zdravko Gabrovski Status feedback => assigned
2020-05-18 20:39 Zdravko Gabrovski Note Edited: 0122911 View Revisions
2020-05-18 20:42 Zdravko Gabrovski Note Added: 0122912
2020-05-18 20:42 Zdravko Gabrovski File Added: LazReportDllBug.7z
2020-05-19 22:39 Jesus Reyes Note Added: 0122945
2020-05-19 22:43 Jesus Reyes Note Edited: 0122945 View Revisions
2020-05-20 18:18 Zdravko Gabrovski Note Added: 0122958
2020-05-20 18:32 Zdravko Gabrovski Note Added: 0122961
2020-05-20 18:32 Zdravko Gabrovski File Added: LazReportDllBug-2.7z
2020-05-20 22:38 Zdravko Gabrovski Note Added: 0122966
2020-05-21 14:16 Jesus Reyes Note Added: 0122979
2020-05-21 15:02 Zdravko Gabrovski Note Added: 0122980
2020-05-21 15:03 Zdravko Gabrovski Note Edited: 0122980 View Revisions
2020-05-21 15:39 jamie philbrook Note Added: 0122982
2020-05-23 09:42 Zdravko Gabrovski Note Added: 0123009
2020-05-23 09:44 Zdravko Gabrovski Note Edited: 0123009 View Revisions
2020-05-23 09:45 Zdravko Gabrovski Note Edited: 0123009 View Revisions
2020-05-23 09:48 Zdravko Gabrovski Note Edited: 0123009 View Revisions
2020-05-23 13:26 Zdravko Gabrovski Note Edited: 0123009 View Revisions
2020-05-23 15:22 emthreex Note Added: 0123012
2020-05-23 20:31 jamie philbrook Note Added: 0123015
2020-05-23 22:07 Zdravko Gabrovski Note Added: 0123024
2020-05-25 19:04 Jesus Reyes Status assigned => resolved
2020-05-25 19:04 Jesus Reyes Resolution open => fixed
2020-05-25 19:04 Jesus Reyes Fixed in Version => 2.1 (SVN)
2020-05-25 19:04 Jesus Reyes Fixed in Revision => 63221
2020-05-25 19:04 Jesus Reyes LazTarget - => 2.2
2020-05-25 19:04 Jesus Reyes Note Added: 0123060
2020-05-25 22:01 Zdravko Gabrovski Note Added: 0123063
2020-05-30 09:38 Zdravko Gabrovski Status resolved => closed
2020-05-30 09:38 Zdravko Gabrovski Note Added: 0123140