I've spent the past few days looking into that and I can't seem to figure it out.
I have a c#
WinForms
application that uses the ReportDocument
to load a report and put it into the Crystal Report Viewer, so that the user can preview it. The purpose is to preview different statistics and the form is never closed. There's a timer that runs and loads different reports into the viewer.
While that happens the memory usage and handles (I can see them in the task manager) keep increasing. When the application is started, it uses around 30 MB and when it runs for 10 minutes, it is then using around 200 MB and it keeps increasing.
I read a lot about this problem on the internet and I found that both the ReportDocument
and the Viewer need to be closed and disposed. Unfortunately, that doesn't fix it. The connection type in the reports is OLE DB(ADO)
as the data is retrieved from an SQL Server
database.
Briefly what happens is that Form1 has a timer that when elapses it disposes the Crystal Reports Viewer
and calls the garbage collector. And then loads the new report.
Here is the sample code that I have
Form1:
private ReportDocument rpt;
private void timer2_Tick(object sender, EventArgs e)
{
timer2.Enabled = false;
try
{
panel1.Hide();
if (rpt != null)
{
foreach (Table t in rpd.Database.Tables)
t.Dispose();
rpt.Close();
rpt.Dispose();
rpt = null;
GC.Collect();
}
panel1.Controls.Remove(CRVviewer);
if (CRVviewer != null)
{
CRVviewer.Dispose();
GC.Collect();
}
// The problem starts from here:
var report = navigationbar1.CurrentNode;
rpt = new ReportDocument();
rpt.Load(@report.Path, OpenReportMethod.OpenReportByDefault);
rpt.ReportOptions.EnableSaveDataWithReport = false;
rpt.SetDatabaseLogon(report.UserId, report.Password);
rpt.VerifyDatabase();
// It ends here
CRVviewer = new CrystalReportViewer();
CRVviewer.ReportSource = rpt;
CRVviewer.ShowLastPage();
pagecount = CRVviewer.GetCurrentPageNumber();
CRVviewer.ShowFirstPage();
panel1.Controls.Add(CRVviewer);
this.Update();
}
catch(Exception ex)
{
ProcessErrors(ex);
}
finally
{
timer2.Enabled = true;
}
}
The problem comes from the DB connections, because if I load a local report, it works fine. But what do I do wrong?
It's very tricky with Crystal Report to clean up the mess it creates with memory. (No offence to SAP)
You will have to first close and dispose the ReportDocument
rpt.Close();
rpt.Dispose();
And then assign nulls to the ReportViewer
and dispose.
CRViewer.ReportSource=null;
CRViewer.Dispose();
CRViewer=null;
And finally, you have to do the two pass GC collect.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Please note that it is generally not recommended to call GC.Collect() but sometimes when memory is too much of an issue and third party COM component's like crystal report has issue with getting disposed properly, we may have to go this route.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With