I've written a small C# application to save reports to pdf using Crystal Reports. The problem I'm having is that as I save more reports, my application's handles continually increase, and I can see new database connections being made and left open after each report is saved. Eventually the application gets an exception from Crystal saying 'database logon error', or I get a C++ runtime error complaining about 'R6025: pure virtual function call'.
I'm looking at application handles using process explorer, as per this technique. I'm checking database connections using MS SQL Server's activity monitor. Each saved report causes about another 100 open handles to 'Semaphores' and 'Events' and 2 database connections.
I believe I'm disposing of the report properly by calling Close() then Dispose(), as mentioned here. Other suggestions around the web include manually closing database connections (Crystal reports - close the database connection) and calling GC.Collect(), however none have worked in my case.
Some environment details
- Visual Studio 2012
- C# Console application targeting .NET 4.5.1 runtime
- Database: MSSQL Server 2012
- Crystal Reports for .NET Framework 4.0, version 13.09.1312
- A Crystal Report document using the SAP database connection (this is important - see answer)
Here's a sample application that exhibits the same problem:
using System;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
namespace ExampleConsoleApp
{
class Program
{
static void Main(string[] args)
{
while (true)
{
SaveReport();
}
}
static void SaveReport()
{
Console.WriteLine("loading report...");
ReportDocument rpt = new ReportDocument();
rpt.Load("test.rpt");
rpt.SetDatabaseLogon("username", "password");
foreach (IConnectionInfo info in rpt.DataSourceConnections)
{
info.IntegratedSecurity = false;
info.SetConnection("SQL", "our_database", "username", "password");
}
rpt.ExportToDisk(ExportFormatType.PortableDocFormat, "test.pdf");
WaitForKeypress("about to dispose report");
// attempt to manually close tables / database links
// none of the following commented code has had any effect
// foreach (TableLink tl in rpt.Database.Links)
// {
// tl.Dispose();
// }
// rpt.Database.Links.Reset();
// rpt.Database.Links.Dispose();
// foreach (Table table in rpt.Database.Tables)
// {
// table.Dispose();
// }
// rpt.Database.Tables.Reset();
// rpt.Database.Tables.Dispose();
// rpt.DataSourceConnections.Clear();
// rpt.Database.Dispose();
rpt.Close();
rpt.Dispose();
// rpt = null;
// GC.Collect();
WaitForKeypress("disposed");
}
private static void WaitForKeypress(string msg = "press a key...")
{
Console.WriteLine(msg);
Console.ReadLine();
}
}
}
Can someone tell me what I'm doing wrong?