Hello,
We are using SAP Crystal Reports for Visual Studio Runtime (Service Pack 15) as our printing engine in a .NET 4.6 Windows Service and we have found out a memory leak that we cannot avoid and it produces a serious memory increase in the long run.
The fact is that it seems that there are several unmanaged objects from the runtime (C++) that are not disposed / free and produces the memory leak because we have not found any memory problem in .NET objects (we have observed it using a Memory Profiler to analyse the process memory).
I've reviewed the code and apply a Fix we found for the previous runtime (CR 11 Runtime versión) but the memory leak persists Image may be NSFW.
Clik here to view..
The code is quite simple and it assures the dipose of the CR objects, I attached the code used to execute the report:
public GReportResult PrintReport(string xmldata, int copies, string layoutpath, string outputpath, string outputformat, string filename, string document, string pathSchema) { int result = 0; string error = string.Empty; try { using (SafeReportDocument report = new SafeReportDocument()) { using (DataSet reportData = new DataSet()) { reportData.Locale = CultureInfo.InvariantCulture; try { reportData.EnforceConstraints = false; using (var xmlSchemaReader = new System.IO.StreamReader(Environment.ExpandEnvironmentVariables(pathSchema))) { reportData.ReadXmlSchema(xmlSchemaReader); using (var xmlDataReader = new System.IO.StringReader(xmldata)) { reportData.ReadXml(xmlDataReader); try { report.Load(Environment.ExpandEnvironmentVariables(layoutpath)); try { report.SetDataSource(reportData); try { switch (outputformat) { case "Raw": report.PrintOptions.PrinterName = outputpath; report.PrintToPrinter(copies, false, 0, 0); break; case "Pdf": report.ExportToDisk(ExportFormatType.PortableDocFormat, filename); break; case "Excel": report.ExportToDisk(ExportFormatType.Excel, filename); break; case "Rtf": report.ExportToDisk(ExportFormatType.EditableRTF, filename); break; case "Html": report.ExportToDisk(ExportFormatType.HTML40, filename); break; } } catch (Exception prn) { //PRINTERROR result = 1; error = prn.Message; } } catch (Exception data) { //PMDATASOURCEERROR result = 2; error = data.Message; } } catch (Exception load) { //PMLAYOUTERROR result = 3; error = load.Message; } } } } } catch (Exception px) { //PMXMLDATAERROR result = 4; error = px.Message; } } } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } catch (Exception ex) { //PMERRINSTANCECMDOCUMENT result = 5; error = ex.Message; } GReportResult reportResult = new GReportResult(result, error); return reportResult; }
And the SafeReportDocument.class (used to fix the memory leak in CR RT 11):
public class SafeReportDocument : ReportDocument, IDisposable { private void CleanGlobalEvents() { Delegate domainUnloadDelegate = (Delegate)typeof(AppDomain).GetField("_domainUnload", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(AppDomain.CurrentDomain); Delegate[] invocationList = domainUnloadDelegate.GetInvocationList(); Delegate ev; for (short i = 0; i < invocationList.Length; i++) { ev = invocationList[i]; if (ev.Target != null && ev.Target.Equals(this)) { AppDomain.CurrentDomain.DomainUnload -= (EventHandler)ev; } } Delegate processExitDelegate = (Delegate)typeof(AppDomain).GetField("_processExit", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(AppDomain.CurrentDomain); invocationList = processExitDelegate.GetInvocationList(); for (short i = 0; i < invocationList.Length; i++) { ev = invocationList[i]; if (ev.Target != null && ev.Target.Equals(this)) { AppDomain.CurrentDomain.ProcessExit -= (EventHandler)ev; } } } /// <summary> /// Cleans up resources /// </summary> /// <param name="disposing"></param> protected override void Dispose(bool disposing) { if (disposing) { this.CleanGlobalEvents(); } base.Dispose(disposing); } }
Is there any issue and fix regarding this case?
Thanks a lot for your time and your support.
Kind regards,
Yamel.