Viewer displays a blank page - c#

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?

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.

Outlook add-in continuously disabled by automatically setting HKCU RegKey to 0 at statup

A client of my outlook add-in started complaining that the add-in does not start at Outlook startup. I took a look, and each time that Outlook is started, the LoadBehaviour is set to 0.
The add-in is written as a managed COM Add-in (sorry, can't change it - legacy tool) with C# 4.0.
It is registered on HKLM for all users.
The client is running Outlook 365 ProPlus Version 1708 (build 8431.22.42 click-to-run) 32 bit (16.0.84.31.2110)
The client machine is Windows 10 64bit.
The HKCU level LoadBehaviour key that keeps getting reset to 0 is generated on the Software\Microsoft\Office.. node (as opposed to under the Software\Wow6432Node...)
The add-in appears under the 'Inactive Application Add-ins' list on Outlook but will not enable through the interface (you can set it, but it does not enable).
Add-in marked as 'always enable' in "slow and disabled COM-add-ins" list.
This is the only Add-in loading through mscoree.dll listed on the Add-in list (I know if there was another mscoree.dll Add-in and that gets disabled, then ours would be disabled as well).
I change the HKCU LoadBehaviour to 3 manually through RegEdit, and it loads the Add-in the next time I open Outlook, but on next restart of Outlook the Add-in is disabled and LoadBehaviour set to 0 once more.
No other client seems to be experiencing the issue, and I am not able to replicate the issue on a local instance of Outlook 365 either.
As always, any help is greatly appreciated!
Cheers!
------EDIT----------
As requested, following is an outline of the actions happening on the OnStartupComplete method of the add-in.
The add-in is meant to provide an Outlook interface for users using the mail-like correspondence exchange component of our larger SaaS service. The add-in connects the user to our server, where they have mail-like items (html formatted) received from other users. The add-in will create a local PST file and create mail items within that PST with the HTML & attachments set as the content. The add-in will also detect if the user is attempting to reply/fwd one of the created 'mails' and override the default process in order to display a web page from the server. The server sync process is triggered at the same frequency as the Outlook Send/receive by adding a handler to the Session.SyncObjects[1].SyncStart event. the sync process itself is handled in a separate BackgroundWorker process.
public void OnStartupComplete()
{
if (_applicationObject == null)
return;
try
{
if (_applicationObject.ActiveExplorer() == null)
return;
_version = _applicationObject.Version.Split(new string[] { "." }, System.StringSplitOptions.RemoveEmptyEntries);
StaticGlobals.OutlookVersionString = _applicationObject.Version;
StaticGlobals.OLIVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version ;
StaticGlobals.OLIFileVersionString = FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).ProductVersion;
//This class has all the processes of my add-in process.
_addinControlObj = new AddInControlerClass();
//Get references to application, explorer objects
_addinControlObj.ApplicationObject = _applicationObject;
_explorers = _addinControlObj.ApplicationObject.Explorers;
//tracks creation of new explorer & inspector windows in order to override reply/fwd actions on specific scenarios (it provides extra options for the user before opening mail editor)
_explorers.NewExplorer += this.Open_NewExplorer;
_currentExplorer = _addinControlObj.ApplicationObject.ActiveExplorer();
_addinControlObj.ApplicationObject.Inspectors.NewInspector += this.Open_NewInspector;
//Create redemption object
_rdoSession = Redemption.RedemptionLoader.new_RDOSession();
_rdoSession.MAPIOBJECT = _addinControlObj.ApplicationObject.Session.MAPIOBJECT;
((Microsoft.Office.Interop.Outlook.ApplicationEvents_11_Event)_applicationObject).Quit += ReleaseComObjects;
((Microsoft.Office.Interop.Outlook.ApplicationEvents_11_Event)_applicationObject).ItemSend += SendMail;
}
catch (System.Exception ex)
{
Common.HandleErrors(ex, "On Connection", false, null, "", StaticGlobals.OutlookVersionString);
}
try
{
OutlookCommands olCommands = new OutlookCommands();
//Get PST file name with Outlook user name suffixed. Common.GetAppPath() will point to a sub folder within either %UserProfile%\AppData\Local or user pre-defined location
_addinControlObj.FilePath = Path.Combine(Common.GetAppPath(), olCommands.GetFileNameUserSpecific(_addinControlObj.ApplicationObject, "addinCreatedDataFileName.pst"));
_addinControlObj.CreateProjectConnections(); // connects to the server to retreive access token for API
}
catch (System.Exception ex)
{
Common.HandleErrors(ex, "On startup complete - Get and validate proj details", true, null, "", StaticGlobals.OutlookVersionString);
}
try
{
//region create PST store and sub-folders
_addinControlObj.CheckAndCreateFolders();
}
catch (System.Exception ex)
{
Common.HandleErrors(ex, "On startup complete - Check and create folders", true, null, "", StaticGlobals.OutlookVersionString);
}
#region add add-in sync method to Outlook send/receive all object
try
{
if (_addinControlObj.ApplicationObject.Session.SyncObjects.Count > 0)
{
_syncObject = _addinControlObj.ApplicationObject.Session.SyncObjects[1];
_syncObject.SyncStart += _syncObject_SyncStart;
}
else
{
Common.HandleErrors(null, "On startup complete - Warning - No SyncObjects accounts found.", false, null, "", StaticGlobals.OutlookVersionString);
}
}
catch (System.Exception ex)
{
Common.HandleErrors(ex, "On startup complete - Add to sync objects", true, null, Properties.Resources.SyncAttachFailMessage, StaticGlobals.OutlookVersionString);
}
#endregion
#region Define the functions for the attachment size limitation.
try
{
_attachmentSizeLimitation.application = _applicationObject;
_attachmentSizeLimitation.sessionRDO = _rdoSession;
_attachmentSizeLimitation.connect = this;
_attachmentSizeLimitation.DefineFunctions();
}
catch (System.Exception ex)
{
Common.HandleErrors(ex, "On startup complete - Initialize attachment limitation.", false, null, "", StaticGlobals.OutlookVersionString);
}
#endregion
}
To start off, I don't think anyone can give a direct answer without some code or error. But maybe some extra checks can help you out.
Add-ins are disabled here in the registry, under this node:
Computer\HKEY_CURRENT_USER\Software\Microsoft\Office\1x.0\Outlook\Resiliency\DisabledItems
(your add-in should not be listed here!)
What might happen is that the add-in crashes while closing.
Sometimes Outlook does not show you a crash/error, to change that behavior this is really helpful:
Set 'VSTO_SUPPRESSDISPLAYALERTS' with value '0' as a system variable
under system > advanced > environment variables,.
https://www.oneplacesolutions.com/support/0053.html
In case the add-in is slow at start-up take a look at, as the Outlook resiliency logic might cause thing behavior.
https://blogs.msdn.microsoft.com/emeamsgdev/2017/08/02/outlooks-slow-add-ins-resiliency-logic-and-how-to-always-enable-slow-add-ins/

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.

Printing multiple report using Crystal Report crash applcation

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();
})
}

Accessing Internet Explorer with Shdocvw results in UnauthorizedAccessException

I'm working on a portion of an application that has a 'finder' tool that allows the user to drag and drop a finder onto an application or internet explorer web browser so that our program can locate it and do what it needs to do. This portion of the application was not under any active development for the last few years (last time I believe IE 7 was the latest browser), but it has worked for IE 9 and below. Starting at IE 10 we get problems, and I specifically am using IE 11.
The following code locates the different Internet explorer windows (including tabs) and file explorer windows. We made a quick VB script for extra testing -
dim objShell
dim objShellWindows
set objShell = CreateObject("shell.application")
set objShellWindows = objShell.Windows
if (not objShellWindows is nothing) then
WScript.Echo objShellWindows.Count
for each win in objShellWindows
wscript.Echo TypeName(win)
WScript.Echo win.LocationUrl
WScript.Echo win.LocationName
WScript.Echo win.HWND
next
end if
set objShellWindows = nothing
set objShell = nothing
The above code will execute without error, and it gives the URL and title bar name of all the tabs we have open.
Below is our C# code. It attempts to get the main IE window (not the tab). this.Handle is the handle of this main IE window that the finder tool gets when the user drops it. We're attempting to iterate through the open windows and find the Internet Explorer window that the user selected. This particular snippet is changed slightly from our original implementation, but the end result is the same. As soon as it hits test = window.Item(i) it throws a UnauthorizedAccessException.
ShellWindows windows = null;
IWebBrowser2 shellWindow = null;
IHTMLDocument2 actualDoc = null;
Type shellApplicationType = Type.GetTypeFromProgID("Shell.Application");
Shell32.Shell shellInterface = (Shell32.Shell)Activator.CreateInstance(shellApplicationType);
windows = (ShellWindows)shellInterface.Windows();
bool found = false;
for (int i = 0; i < windows.Count; i++)
{
try
{
object test;
try
{
test = windows.Item(i); //Exception here
shellWindow = (IWebBrowser2)test;
}
catch
{
}
if (shellWindow != null && (IntPtr)shellWindow.HWND == this.Handle)
{
//the rest of the code gets the correct tab from the main IE window
This is the original unedited code from when we first revisited this portion of the program.
ShellWindows windows = null;
IWebBrowser2 shellWindow =null;
IHTMLDocument2 actualDoc = null;
windows = new SHDocVw.ShellWindowsClass();
bool found = false;
for (int i = 0; i < windows.Count; i++)
{
try{
shellWindow = windows.Item(i) as SHDocVw.IWebBrowser2; //Exception here
if (shellWindow != null && (IntPtr)shellWindow.HWND == this.Handle)
{
I would also like to note that instead of a for loop I have tried a foreach loop that followed this syntax
foreach(IWebBrowser2 browser in windows)
and
foreach(InternetExplorer browser in windows)
In those instances, the loop skips over the IE window.
I have looked at IE's security settings. I have disabled Enhanced Protection Mode and allowed cross domain. There does not seem to be a lot of information on this issue, and every approach we try seems to always end up with an UnauthorizedAccessException.
Edit: In response to Hans answer, I do not have any anti malware running on this machine. It is a virtual machine with windows 7 that has the latest SP and updates (no microsoft security essentials). I tried running the app on a 32 bit machine with similar settings and it also failed. After testing it on IE 11, I uninstalled IE 11, rebooted, tried IE 10 (it failed, same error), uninstalled IE 10, rebooted, tried IE 9, and it worked without making any other changes to the system.
Here is the SSCCE as requested by Hans. This code actually works on my target machine. I will return to this post in the next day or two while I handle other tasks that require my attention.
I build this as a 32 bit console app, and ran it on a 64bit machine.
using System;
using SHDocVw;
using mshtml;
namespace GetBrowserSSCCE
{
class Program
{
static void Main(string[] args)
{
ShellWindows windows = null;
IWebBrowser2 shellWindow = null;
windows = new SHDocVw.ShellWindowsClass();
for (int i = 0; i < windows.Count; i++)
{
try
{
shellWindow = windows.Item(i) as SHDocVw.IWebBrowser2;
Console.WriteLine(string.Format("Window Found. HWND: {0}\nName: {1}", shellWindow.HWND, shellWindow.Name));
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("UnauthorizedAccessException caught. Exception Text: " +ex.Message);
}
}
Console.ReadLine();
}
}
}
That this fails in your C# program but not when you run it from the VBScript interpreter points strongly to an environmental problem. Both use the exact same shell interface. The specific exception (underlying error code is 0x8007005) has been reported before on the interwebs but never diagnosed.
First thing you should focus on whenever you have an environmental problem, particularly the kind associated with access rights, is the anti-malware installed on the machine. Disable it and try again.
Second one you should focus on is a quirk associated with ShellWindows, it doesn't just enumerate Internet Explorer windows but also Windows Explorer windows. You've been looking at having sufficient access to IE but that isn't enough, this code can also fail if you happen to have an Explorer window opened and there's an access problem with the explorer.exe process. Do note that your Activator.CreateInstance() method call is not equivalent to the VBScript code, Activator.GetObject() is. So your changes would actually make the problem worse if this is underlying problem.
Third detail that's worth checking is the bitness of your process. By default, your VBScript code will run in the 64-bit script interpreter on a machine that boots the 64-bit version of Windows. But the default setting for a C# project is to run in 32-bit mode. Right-click your EXE project, Properties, Build tab and tinker with the Platform target setting, the 'Prefer 32-bit' checkbox if you see it. This is not an explanation for the error code, it can however affect the effectiveness of anti-malware to intrude.

Categories