Printing multiple report using Crystal Report crash applcation - c#

I have a WinForms Application (.net 4.0, x86) with Crystal Reports (13.0.15). I want to print multiple documents using Crystal Reports (without seeing them on Report Viewer - directly on printer).
foreach (var document in documents )
{
ReportDocument report=GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
}
Everytime it prints 48 reports and on 49th print I've got exception System.ComponentModel.Win32Exception The handle is invalid Void OnStartPrint(System.Drawing.Printing.PrintDocument, System.Drawing.Printing.PrintEventArgs)
I tried another set of documents but I still have exception on 49 print.
I tried to change PrintJobLimit to over 9000 but it doesn't help.
Next I tried to dispose report after print:
foreach (var document in documents )
{
using (ReportDocument report=GenerateReport(document.id))
{
report.PrintToPrinter(printerSettings, pageSettings, false);
}
}
But now program crashes before print 28th report and I cannot catch exception (program stop working). Only in Event Viewer is information about Application Error (Faulting module name: ntdll.dll).
I tried different things (and combination of them):
report.Close();
report.Dispose();
report=null;
even GC.Collect() after print but it still doesn't work.
Anybody has a solution?

It seems like when report.PrintToPrinter returned, the underlying operation sometimes went on. You need to keep reportDocument alive for a few more time, instead of let it go out of scope and GC collection kick in.
My solution is to put them into a delayed queue and Close them later, something like:
foreach (var document in documents )
{
ReportDocument report = GenerateReport(document.id);
report.PrintToPrinter(printerSettings, pageSettings, false);
Task.Run(async () => {
// keep report alive for extra 60 secs
await Task.Delay(60 * 1000);
report.Close();
report.Dispose();
})
}

Related

LocalReport hangs sometimes on render

When generating a RDLC report in a C#/WPF application, sometimes it hangs on render (call of render method doesn't return).
The report data comes from a C# object (which is filled in advance with data from a PostgreSQL database) so this issue is not SQL related.
I wonder:
How can I find out the reason?
Is there any way to enable the user to kill the report generation when it hangs?
Embedding the report generation in a using block (as seen in ReportViewer rendering hangs server after some executions) didn't solve the problem.
using (LocalReport report = new LocalReport())
{
report.ReportPath = #"C:\Path\To\MyReport.rdlc";
// Get the report data from db and fill it into MyReportData object.
MyReportData data = CreateReportData();
ReportDataSource headerDataSource = new ReportDataSource();
headerDataSource.Name = "HeaderDataSet";
headerDataSource.Value = data.Header;
report.DataSources.Add(headerDataSource);
// Add more data sources ...
// No report parameters are being used
report.Refresh();
pdfData = report.Render("PDF"); // here it hangs sometimes
}
As said above, the render-call hangs sometimes. After killing the application and generating exactly the same report again it works.

Crystal Reports 8.5 Sections prevents file from being deleted in C# using COM

I have an created a command line application which takes a crystal report file, opens it, exports it to a text file and then cleans up after itself by deleting the crystal report. This works fine until I suppress sections in the crystal report, then when I try and delete it I get the following:
System.IO.IOException: The process cannot access the file 'C:\ExportCMD\bin\Debug\ReportExport\2ee373f0-e05a-42d6-9b61-9c79e2662c20\14_636271819978854269.rpt' because it is being used by another process.
After some investigation I have found that it happens when I suppress a section in the open report, if I comment out that code it works fine. The code for setting the Suppress flag is:
private static void SuppressReportSection(ref Report openReport, string sectionToFind, bool hideSection)
{
if (!string.IsNullOrWhiteSpace(sectionToFind) && openReport != null)
{
openReport.Sections[sectionToFind].Suppress = hideSection;
}
}
After checking Google for a solution, I gave the following a try:
private static void SuppressReportSection(ref Report openReport, string sectionToFind, bool hideSection)
{
if (!string.IsNullOrWhiteSpace(sectionToFind) && openReport != null)
{
Sections reportSections = openReport.Sections;
try
{
if (reportSections != null)
{
reportSections[sectionToFind].Suppress = hideSection;
}
}
catch
{
throw;
}
finally
{
if (reportSections != null)
{
Marshal.ReleaseComObject(reportSections);
reportSections = null;
}
}
}
}
This unfortunately didn't cure it either. I have tried it with and without the ref in case it is something to do with the report object.
I am having to use the Crystal Reports 8.5, and I have added the reference.
When I destroy my report object I call the Marshal.ReleaseComObject, then GC.WaitForFullGCComplete() in the hope that it will have released the file. Once all this has completed I call the cleanup code which deletes the files. The clean up method will allow multiple attempts at deleting the file before it throws the error.
Where am I going wrong? If there is already an answer to the question can you point me to it please.
I am using C# with .Net 4 as this is the highest version we can get on the servers. I can't use the CrystalDecisions assemblies.
Try to use FinalReleaseComObject instead:
Therefore, use the ReleaseComObject only if it is absolutely required. If you want to call this method to ensure that a COM component is released at a determined time, consider using the FinalReleaseComObject method instead. FinalReleaseComObject will release the underlying COM component regardless of how many times it has re-entered the CLR. The internal reference count of the RCW is incremented by one every time the COM component re-enters the CLR. Therefore, you could call the ReleaseComObject method in a loop until the value returned is zero. This achieves the same result as the FinalReleaseComObject method.
reference
It's all still in your task manager all you have to do is..
Just remove the file in task manager using task kill by opening a .bat file. You can do this after you execute or before deleting the report.. after this you are freely can delete the file or you can open it again without any error.

Viewer displays a blank page

We use PBNI to call methods and open windows in C#, using a C++ DLL and Crystal Reports 10.
A window that contains a Crystal Reports Viewer OLE control shows nothing but a blank page, with no error. In debug mode in Powerbuilder, the report shows correctly. (Printing and exporting data work but not the viewer.)
Using Process Monitor, we can see that the C# executable seeks and finds many Crystal Reports dlls (including crviewer.dll) - The reports should connect to an Oracle database through ODBC. Consulting Oracle SQL history, I think that the viewer can’t connect to the database. But when I change the connection credentials (alias, id…) and try to get the SQLQueryString in the report, I have an error (so the credentials are correct.)
Powerbuilder code (Open event of the window):
OLEObject APP_CR, APPLICATION
OLEObject CONNECTION_INFO
APPLICATION = CREATE OLEObject
long li_ret
li_ret = APPLICATION.ConnectToNewObject('CrystalRuntime.Application.10')
APP_CR = APPLICATION.OpenReport("C:\Apps\PB120\HeliosII\Dev\Etat\CLI_LST.rpt", 1)
CONNECTION_INFO = APP_CR.database.tables[1].ConnectionProperties
CONNECTION_INFO.DeleteAll
CONNECTION_INFO.Add("DSN","DATABASE O10")
CONNECTION_INFO.Add("Database", "DATABASE")
CONNECTION_INFO.Add("User ID","ETAT")
CONNECTION_INFO.Add("Password","ETAT")
// Testing the connection
string ls_SQLQuery
ls_SQLQuery = APP_CR.SQLQueryString
APP_CR.SQLQueryString = ls_SQLQuery
// Showing the report
ole_CRViewer.object.ReportSource(App_CR)
ole_CRViewer.object.ViewReport
C++ Dll code (calls a function that opens the window)
pbgroup group = session->FindGroup("nvo_lanceur", pbgroup_userobject);
if (group == NULL) return false;
pbclass cls = session->FindClass(group, "nvo_lanceur");
if (cls == NULL) return false;
pbobject pbobj = session->NewObject(cls);
if (pbobj == NULL) return false;
pbmethodID mid = session->GetMethodID(cls, "of_traitermessage", PBRT_FUNCTION, "QS");
PBCallInfo ci;
session->InitCallInfo(cls, mid, &ci);
ci.pArgs->GetAt(0)->SetString(parameters);
// Call the function
try
{
session->InvokeObjectFunction(pbobj, mid, &ci);
// Was PB exception thrown?
if (session->HasExceptionThrown())
{
// Handle PB exception
session->ClearException();
}
}
catch (...)
{
// Handle C++ exception
}
The "of_traitermessage" function of the "nvo_lanceur" object calls an Open of the window that contains the viewer (and many other windows, depending on parameters.) The C# application calls a C++ method ever 100ms which calls a "session->ProcessPBMessage();" for dispatching powerbuilder event messages. How can I make this report display?

How to check in ReportViewer if a report exists to prevent rsItemNotFound?

I'm loading a report into my ReportViewer 10.0 along these lines:
MyReportViewer.ServerReport.ReportPath = "/My.Report.Folder/DoesNotExist";
// The following call throws a ReportServerException ("...rsItemNotFound...")
var requestedParameters = MyReportViewer.ServerReport.GetParameters();
var filledParameters = GetFilledParameters(requestedParameters);
MyReportViewer.ServerReport.SetParameters(requestedParameters);
MyReportViewer.ServerReport.Refresh();
This works fine, except when the report does not exist. In that case the GetParameters() call will fail with a ReportServerException and a message along these lines:
The item '/My.Report.Folder/DoesNotExist' cannot be found. (rsItemNotFound)
Of course I could use a try...catch block here to trap this situation, but I'd rather explicitly check if a report exists. Is there a way to do this using the ReportViewer component? Or do I need to hook into the SSRS web services?
I've gone through MyReportViewer.ServerReport's methods and properties but found nothing that seems of use. I've also checked this related question, but it's not really in the context of the ReportViewer control.
I'm open to other answers, but until then, here's my current workaround:
try
{
var requestedParameters = MyReportViewer.ServerReport.GetParameters();
var filledParameters = GetFilledParameters(requestedParameters);
MyReportViewer.ServerReport.SetParameters(requestedParameters);
MyReportViewer.ServerReport.Refresh();
}
catch (ReportServerException exception)
{
if (!exception.Message.Contains("rsItemNotFound"))
{
throw;
}
// Else... ReportViewer will show "rsItemNotFound" error.
// Or you can do something fancy in your app here, I suppose :-)
}

Detect Document Reload has occured on server

I have an winform/OCX that consumes a qlikview document. We have gotten a patch from QV so that RefreshDocument works in the OCX as the RefreshDocument does in QV application. But the Application shows a nice enabled button when the document has been reload on the server.
Does anyone know what needs to be done to detect that. Either in C# or in macro code or ManagementAPI ?
This is the ReloadDocument Code.
private void button2_Click(object sender, EventArgs e)
{
var myBloodybookmarkHack = "dynaBookmark" + Guid.NewGuid().ToString().Replace("-","");
axQlikOCX1.ActiveDocument.CreateUserBookmark(myBloodybookmarkHack, true);
//axQlikOCX1.OpenDocument(#"qvp://qvSeverName/path/MyDocument.qvw?bookmark=Server\dynaBookmarkb5aa82ae467540fdb0d18bb499044ed9");
axQlikOCX1.RefreshDocument();
axQlikOCX1.ActiveDocument.RecallUserBookmark(myBloodybookmarkHack);
axQlikOCX1.ActiveDocument.RemoveUserBookmark(myBloodybookmarkHack);
}
By suppressing the paint event I get this to run pretty ok. Next patch will include that it keeps the selections (Will be fixed in 11.2 servicerelease 6).
You need to detect if CreateUserBookmark was successfull or not and not restore the bookmark if the creation failed.
This code works in QV 11.2 serviceRelease 5.
The filesystem reads a new modified time when the qvw file is rewritten after load. Assuming the data portion of this application is not broken out from the QVW file. Likely, you could come very close to accomplishing this by checking for new timestamps. Alternatively, if logging is enabled in the qvw document you could log read the text file* that QlikView generates to accomplish the same thing.
*The text file writes are delayed sometimes so your file might be refreshed a little bit before the log states that it is.
We ended up using the QV Management api to get the last task reload time
Download the Qv management api demo from QV
This code shows you how to get tasks on a document. Through that you get when "last document reload task" was finished.
private DateTime GetLastDocumentRun(string documentName)
{
string QMS = "http://MyQlikviewserver:4799/QMS/Service";
var client = new QMSClient("BasicHttpBinding_IQMS", QMS);
string key = client.GetTimeLimitedServiceKey();
ServiceKeyClientMessageInspector.ServiceKey = key;
var taskStatusFilter = new TaskStatusFilter();
var clientTaskStatuses = client.GetTaskStatuses(taskStatusFilter, TaskStatusScope.All);
foreach (var taskStatus in clientTaskStatuses)
{
Trace.WriteLine(taskStatus.General.TaskName);
if (taskStatus.General.TaskName.ToLower().Contains(documentName.ToLower()))
{
string fin = taskStatus.Extended.FinishedTime + "";
DateTime finishedTime;
if (DateTime.TryParse(fin, out finishedTime))
return finishedTime;
Logger.ErrMessage("QvManagementApi.GetLastDocumentRun",new Exception("Task finished time did not return a valid datetime value:" + fin));
return DateTime.MinValue;
}
}
return DateTime.MinValue;
}
This is slow, so you should run on a different thread.
Also this does not show if the task is successfully reloaded. We haven't fix that yet but on taskStatus.Extended you have the last log, which you can text parse to get if it was successfully reloaded or not.
If I understand correctly you want to know if a document has finished reloading on a QlikView server right?
I've you OCX application has a constant connection, you could evaluate the ReloadTime() function in the document which would tell you when the document was last reloaded. If you listen for the function and issuing a DocumentRefresh while doing this, then you would get a changed timestamp once the newly reloaded document becomes avaible on the server.
The code your posting, does not reload a QlikView document. At least not in QlikView lingo, it just open the documents on the server.
Please elaborate if I misunderstand you.
Regards Torber

Categories