Usage of GDI32 in service process while drawing barcode (TppDrawBarCode)
We have (quite unpredictable) issues when using TppDrawBarCode in reports that are streamed to PDF in a Windows service. The situation where we get into is that generating a certain report in a background thread in a Windows service suddenly stops functioning correctly. This can only be resolved by restarting the server (restarting processes have no effect). Interesting enough is that, when we are in this failure state, generating the same report in interactive (UI) mode works flowless, but generating it from a background service creates a high CPU spike and the thread that is generating the report hangs. Running other Windows services generating the same report give the same issues when the OS/system is in this failure state. Interesting is that streaming a report as PDF !without! this barcode component works fine.
In this failure state, the generating thread always hangs on Vcl.Graphics.TResourceManager.FreeResource (in the GDI32 call to _DeleteObject) with a high CPU spike (some internal loop in GDI?). After rebooting the server, the exact same code works perfectly for very long periods (days/weeks). We have seen that these issues surface after a period of time and this strengthen us in the conclusion that the GDI heap might get corrupted, or is incorrectly used in the barcode component, which causes this "corruption". We have multiple callstacks that all narrows down to the mentioned code (GDI32 _DeleteObject). Correct usage of GDI in a non-UI Windows service in background threads is challenging and I suspect that the issue origins from how this is used in the barcode component.
After investigating the issue our suspicion is in (incorrect) usage of GDI32 in this barcode component. When generating the report (as PDF) TppPDFRendererGeneric.RenderToPDF calls TppDrawBarCode.AsMetaFile. Here we start doing GDI32 calls, for example setting the TBrush.SetColor or TCanvas.SetFont which are (sometimes) done on invalid/corrupted GDI handles. We have evidence of failures in at least the following two cases (callstacks) and here the thread hangs (with high CPU) in the TResourceManager.FreeResource.
In this failure state, the generating thread always hangs on Vcl.Graphics.TResourceManager.FreeResource (in the GDI32 call to _DeleteObject) with a high CPU spike (some internal loop in GDI?). After rebooting the server, the exact same code works perfectly for very long periods (days/weeks). We have seen that these issues surface after a period of time and this strengthen us in the conclusion that the GDI heap might get corrupted, or is incorrectly used in the barcode component, which causes this "corruption". We have multiple callstacks that all narrows down to the mentioned code (GDI32 _DeleteObject). Correct usage of GDI in a non-UI Windows service in background threads is challenging and I suspect that the issue origins from how this is used in the barcode component.
After investigating the issue our suspicion is in (incorrect) usage of GDI32 in this barcode component. When generating the report (as PDF) TppPDFRendererGeneric.RenderToPDF calls TppDrawBarCode.AsMetaFile. Here we start doing GDI32 calls, for example setting the TBrush.SetColor or TCanvas.SetFont which are (sometimes) done on invalid/corrupted GDI handles. We have evidence of failures in at least the following two cases (callstacks) and here the thread hangs (with high CPU) in the TResourceManager.FreeResource.
_DeleteObject@4
TResourceManager.FreeResource
TResourceManager.ChangeResource
TBrush.SetData
TBrush.SetColor
TppDrawBarCode.AsMetaFile
TppPDFRendererGeneric.RenderToPDF
TppPDFDevice.DrawToPage
TppPDFDevice.SavePageToFile
TppFileDevice.ReceivePage
We have tested this in ReportBuilder 23.02, 22.06 and 22.02 when the system is in the failure state and all give the same issues.
[77614628]{GDI32.dll } pldcGet
[05237E49] Vcl.Graphics.TResourceManager.FreeResource
[05237F45] Vcl.Graphics.TResourceManager.AssignResource
[052386BC] Vcl.Graphics.TFont.Assign
[0523A589] Vcl.Graphics.TCanvas.SetFont
[075C91B6] ppBarCodDrwCmd.TppDrawBarCode.DrawBarCode (Line 985, "ppBarCodDrwCmd.pas")
[075C765B] ppBarCodDrwCmd.TppDrawBarCode.CalcBarCodeSize (Line 462, "ppBarCodDrwCmd.pas")
[075C7421] ppBarCodDrwCmd.TppDrawBarCode.AsMetaFile (Line 389, "ppBarCodDrwCmd.pas")
[0502D3DF] SysUtils.AnsiCompareText
[050D72EA] Classes.TStringList.CompareStrings
[050D6BD8] Classes.TStringList.IndexOf
[05114EE3] SyncObjs.TCriticalSection.Release
[0502D3DF] SysUtils.AnsiCompareText
[05114EE3] SyncObjs.TCriticalSection.Release
[073CA4CE] ppPrintr.TppCustomPrinter.GetDC (Line 3672, "ppPrintr.pas")
[073CA4EB] ppPrintr.TppCustomPrinter.GetDC (Line 3675, "ppPrintr.pas")
[0502D3DF] SysUtils.AnsiCompareText
[050D72EA] Classes.TStringList.CompareStrings
[050D6BD8] Classes.TStringList.IndexOf
[05114EE3] SyncObjs.TCriticalSection.Release
[0502D3DF] SysUtils.AnsiCompareText
[05114EE3] SyncObjs.TCriticalSection.Release
[073CA4CE] ppPrintr.TppCustomPrinter.GetDC (Line 3672, "ppPrintr.pas")
[073CA4EB] ppPrintr.TppCustomPrinter.GetDC (Line 3675, "ppPrintr.pas")
[07609282] ppPDFRendererGeneric.TppPDFRendererGeneric.RenderToPDF (Line 79, "ppPDFRendererGeneric.pas")
[075BCC85] ppPDFRenderer.TppPDFRenderer.SetGraphicsCanvas (Line 178, "ppPDFRenderer.pas")
[076368A5] ppPDFDevice.TppPDFDevice.DrawToPage (Line 702, "ppPDFDevice.pas")