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 .
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.