SAP Crystal Reports for Visual Studio

Hello guys

My .Net developments were ALWAYS at risk! Whenever I am doing it good, the project gets cancelled & I return to the Oracle world.

Anyway, for the last project that is about be shelved, I chose SAP Crystal Reports in addition to Microsoft’s RDLC for few reasons. Microsoft has stopped shipping Report components with their IDE Visual Studio & getting it work by installing Nuget packages and extension methods are not going to be very easy for most of the newbies (I am a life long newbie when it is all about .NET development)

On the other hand, installing & going online with SAP Crystal Reports for Visual Studio is pretty straight forward. Download the package from SAP, install it and you are all set to go. Well definitely not!

Let us quickly see how to install & get the most out of SAP Crystal Reports.

  1. Installation
  2. Server Runtime
  3. Your 1st Crystal Report
  4. POST BACK and hacks

Installation

Register & download SAP Crystal Reports for Visual Studio. Make sure you install the runtime engine which is prompted during the installation. Simple as it is.

Server Runtime

If you are publishing your application with SAP reports from another computer/Server make sure you install the Runtime package.

If you’ve installed the runtime, this will create the below structure under inetpub\wwwroot folder

Your 1st Crystal Report

If you are a .Net developer, already familiar with dragging and dropping controls from the Toolbox node(s). Crystal Reports is not different. Just drag and drop “CrystalReportViewer” control to your page & you are all set to go.

Dropping the control to the page adds many references to your project. You may scroll through the references to identify them. All the references have “Crystal” in the name.

Now let us create a sample report & view the report on the web.

If this is your 1st attempt to create a report, select the default & continue. Based on the connections already available in the environment, you will be provided existing connections or an option to create a new connection.

Here is the catch. If the database (MSSQL or other) is configured for integrated security & you prefer to continue using the same for your project, you must make sure that from your webform/page you will be sticking to the same authentication method. Said, you cannot use integrated authentication during design and username/password login during the runtime. So be careful when you are creating connections.

We will create a new connection using username and password to local MS SQL server.

You don’t have to change anything here. Just click the “Finish” button to complete the connection.

That’s it. This creates a new connection and the same will be available under “My Connections”.

Now from the connection, you can select a table, view or stored procedure for your report as source. I have selected a table “bal2020” as you could see with the below image.

Once you click the OK button, you will be taken back to object browser window.

You can see that the table you have selected is shown under “Database Fields” node and expanding the Table node will show you the available columns those you could add to the report.

Drag and drop the columns you want to add in the “Section 3 (Details)” area and the act creates relevant titles in the “Section 2 (Page Header)” area. You can always modify the titles.

Unlike RDLC, you can immediately preview your reports from the design window itself by switching to “Main Report Preview” tab.

Let us see how this report viewed from a webform/page.

As we are trying to create a report for the first time, I suggest you to use the GUI for linking your newly created report with the webform/page.

Select “New Report Source” form “Choose Report Source” and select your newly created report. Leave the names as seen for this exercise.

Prior attaching the report, the Crystal Report Viewer control code was minimal like below

<body>
    <form id="form1" runat="server">
        <div>
            <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="true" />
        </div>
    </form>
</body>

and after linking the report to the viewer, you will notice that a number of elements are added to the viewer.

 <form id="form1" runat="server">
        <div>
            <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="True" 
                GroupTreeImagesFolderUrl="" Height="1202px" ReportSourceID="CrystalReportSource1" 
                ToolbarImagesFolderUrl="" ToolPanelWidth="200px" Width="1104px" />
            <CR:CrystalReportSource ID="CrystalReportSource1" runat="server">
                <Report FileName="CrystalReport1.rpt">
                </Report>
            </CR:CrystalReportSource>
        </div>
    </form>

As there are not complex code involved as in with stored procedures, your page will load the report. You can save the webform & try to see whether it truly gets loaded on the browser.

Save everything & build the solution & debug the page that you have just created. Do not use Internet Explorer for debugging. You may end up with “N” number of errors by using IE.

Something is wrong, right? let us see why the report is not loading. With this specific case, an empty page in the place of report viewer is due to missing runtime binaries. If you remember, I asked you to install the runtime while installing the report developer. So there is something wrong…

I don’t have the technical knowledge to confidently say it is a bug , however looks like one, with certain solutions, the symbolic link to rootdrive:\inetpub\wwwroot\aspnet_client is not created within the solution folder after Crystal Report Viewer control is added. Failing to find “aspnet_client” symbolic link to the actual path where the runtime binaries are kept results in failing to load the report on the browser, unfortunately without generating visible errors. While missing the symbolic link being one of the major reasons, there could be few other reasons as well like application pool configured for both 32 Bit & 64 Bit. I have noticed that if your default application pool is configured to cater both 32 & 64 Bit, the 64 Bit runtime will not load & errors will be generated while trying to load reports. If you have installed 64 Bit SAP components, create a new application pool exclusively for 64 Bit only.

To resolve the issues due to runtime binaries, we can either create a symbolic link to “rootdrive:\inetpub\wwwroot\aspnet_client” or copy the entire aspnet_client folder to project, which is approximately 17-18 MBs in size.

I will always prefer the 1st option. So let us see how we can create a symbolic link to “rootdrive:\inetpub\wwwroot\aspnet_client” from our project.

The above symbolic link is explained as below.

Your project is created under user’s home folder. For example, my username is Rajesh. Hence Visual Studio has created a path “sources\repos” where all new solutions will be created and stored (unless I change the VS options)

As I have chosen “CrystalReportsDemo” as my solution name, a folder with the same name is created inside “sources\repos” and the solution related files and folders are kept within a subfolder with the solution name. Finally, the path will look like below once after the solution is created.

"C:\Users\Rajesh\source\repos\CrystalReportsDemo\CrystalReportsDemo"

We are expected to create the symbolic link to “aspnet_client” folder inside the “CrystalReportsDemo” subfolder.

The symbolic link must have a name, hence you will provide “aspnet_client” as the link name (no other names please!) and refer the original path of aspnet_client as source.

mklink /D "C:\Users\Rajesh\source\repos\CrystalReportsDemo\CrystalReportsDemo\aspnet_client" "C:\inetpub\wwwroot\aspnet_client"

I know it sounds bit complex and I hope you will get a hang of it with practice. A successfully created symbolic link will look like below. If you have grouping enabled for the files in explorer, the newly created symbolic link will be listed under folders.

Let us try to view the webform once again after creating the symbolic link.

Basically, when you add a crystal report to your solution, it is treated as an “Embedded Resource” & not copied to output directory. Unless you change these properties, your published application will not able to access the reports. Let us see quickly how these changes are made.

Now the output directory will copy the report files each time the solution is built.

PostBack & hacks

What is PostBack? PostBack is the name given to the process of submitting an ASP.NET page to the server for processing. This has a big effect on how Crystal Reports behave.

Consider a case when you have a webform that has few user choices & a crystal report attached to it. By default much of the ASP.Net controls support “AutoPostBack” property & very useful when a developer wants to refer to other controls values programmatically. Well, each instance of AutoPostBack causes the Crystal Report to refresh itself.

As you are already aware ASP.Net is stateless and Crystal Report will loose all functionalities like progressing to next page or previous page etc when associated buttons on the toolbar is pressed immediately after a postback. In addition to losing these functionality, you may be prompted to enter the database connection details. SAP recommends using Page_Init() instead of Page_Load() method in addition to using session variables to load the report after postback. We will see a complex sample now, which is from a production environment.

Scope of the report

Generate a report for Finance department that lists all salary elements for a chosen company, department & few other selections by the end user.

So this particular report has a total of 5 parameters received from the user & clicking the “Print Report” should show the report with relevant data.

The above report uses multiple stored procedures to fetch the relevant data for the report & copying the script here will defy the intend. Hence I will copy a sample that I posted as an answer to my own question over StackOverFlow & explain it.

You may refer to the stackoverflow thread here.

The below example generates the same report sample you have seen above, however much of the parameters are hardcoded & the only user interactable control is the button. This report uses database stored procedures for report data.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="c4.aspx.cs" Inherits="CrystalTest.c4" %>
<%@ Register Assembly="CrystalDecisions.Web, Version=13.0.4000.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" Namespace="CrystalDecisions.Web" TagPrefix="CR" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Button ID="Button1" runat="server" Text="Print Report" OnClick="Button1_Click" />
            <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="true" />
        </div>
    </form>
</body>
</html>

Code behind for the webform is like below:

using CrystalDecisions.CrystalReports.Engine;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace CrystalTest
{
    public partial class c4 : System.Web.UI.Page
    {
        protected void Page_Init(object sender, EventArgs e)
        {
            if (IsPostBack)
            {
                CrystalReportViewer1.ReportSource = (ReportDocument)Session["Report"];
            }
        }
        private void ShowReport1()
        {
            string ConnectionString = ConfigurationManager.ConnectionStrings["menass"].ToString();
            using (SqlConnection con = new SqlConnection(ConnectionString))
            {
                using (SqlCommand cmd = new SqlCommand("GETMONTHSALARY", con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.Add("@pProcessYear", SqlDbType.Int).Value = 2020;
                    cmd.Parameters.Add("@pProcessMonth", SqlDbType.Int).Value = 1;
                    cmd.Parameters.Add("@pProcessSection", SqlDbType.VarChar).Value = "9";
                    cmd.Parameters.Add("@pProcessSite", SqlDbType.VarChar).Value = "1";
                    cmd.Parameters.Add("@pProcessCatg", SqlDbType.VarChar).Value = "1";
                    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                    DataSet ds = new DataSet();
                    adapter.Fill(ds, "SalaryDT");
                    ReportDocument oRpt = new ReportDocument();
                    oRpt.Load(Server.MapPath(@"~/dataset/CrystalReport1.rpt"));
                    oRpt.DataSourceConnections.Clear();
                    oRpt.SetDataSource(ds);
                    oRpt.Subreports[0].SetDataSource(FillOverTime());
                    CrystalReportViewer1.Visible = true;
                    CrystalReportViewer1.ReportSource = oRpt;
                    Session["Report"] = oRpt;
                }
            }
        }
        private DataSet FillOverTime()
        {
            string ConnectionString = ConfigurationManager.ConnectionStrings["menass"].ToString();
            using (SqlConnection con = new SqlConnection(ConnectionString))
            {
                using (SqlCommand cmd = new SqlCommand("GetEmployeeOverTime", con))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.Add("@pEmployeeCode", SqlDbType.VarChar).Value = DBNull.Value;
                    cmd.Parameters.Add("@pProcessYear", SqlDbType.Int).Value = 2020;
                    cmd.Parameters.Add("@pProcessMonth", SqlDbType.Int).Value = 1;
                    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                    DataSet ds1 = new DataSet();
                    adapter.Fill(ds1, "OverTimeDT");
                    return ds1;
                }
            }
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            ShowReport1();
        }
    }
}

The above sample uses 2 different stored procedures to generate data for main report and a sub report. ShowReport1() method is triggered when the button is clicked. Once the report is generated, it is saved into a session object & whenever a post back happens, this saved object is assigned to report source. Simple as it is. Each button click on the reports toolbar is treated as post back, triggering the report source being assigned again and again. You might notice that I don’t have a Page_Load() method with the code behind!

I know this is just a beginning & including more detailing might make the post irrelevant. With my next post, I will try to explain Crystal Reports parameters, sub-reports etc.

2nd Part. Passing parameters to Crystal Reports

regards,

rajesh

Advertisements

Windows 10 | parameter is incorrect

Hi guys

I can’t confidently say whether it is the KB4557957 or something that deliberate by me, I had serious troubles connecting to IKEV2 VPN, ending with a message “Parameter is incorrect”.

Although I landed on the solution earlier, I disrespected the order & spent another day trying to figure out the “issues”. Finally I did as the post asks & “fixed” the problems. Enough talks, just follow the instructions as mentioned on this page

https://github.com/trailofbits/algo/issues/1051

regards,

rajesh

Windows 10 | Stuck in the boot

Hi guys

I hardly had any issues with Windows 10, to conclude my experience with this specific OS. I’ve started using the OS from the 1st preview builds, was an insider etc.

Yesterday I couldn’t start the OS. Windows logo loads, OS loading animation starts and that’s all. After breaking the load process thrice, Windows initiated the Automatic Repair process and loaded the OS after a considerable amount of time.

As a practice, from years, I always move the Documents and Download folders to a different partition for my user account. This saves me additional time to backup and restore the user files when I need to rebuild the box.

Once Windows finally started, from experiences, I started checking for the partitions and found that two of the partitions on which I had the Documents and Downloads folders “WERE” missing. Now the difficult task was to identify the physical disk. My machine has total 6 disks (2 SSD, 4 various capacity mechanical drivers & couple of them 6+ years old) & after a thorough examination, realized that the disk that was having the “Downloads” and “Documents” folders has a SATA cable that was not anymore holding up it’s physical connection on the port. Once changed, everything went back to normal. Actually the user profile was trying to access the missing disk, causing the lockup during boot.

So, if you have heavily customized desktop computers once in a while, make sure that your connections are in place. Every time you open the case, make sure you are having all the cables checked and insured that tightly secured!

regards,

Windows 10 | Java client will not load on Internet Explorer

Hello guys

Internet explorer? Java client? Yes! Oracle E-Business Suite (Oracle Apps) uses NPAPI client when accessed through a web browser for Oracle Forms/Reports based applications. Currently the only one browser that allows the NPAPI client to load is Internet Explorer.

Depending upon the complex JAVA dependencies your version of Oracle Applications must be supporting a particular version of java client. Although you can use switches to load latest versions of JRE for your legacy Oracle application, not always advisable as I’ve experienced non-responsiveness & freezing.

Windows 10 version 2004 (Released by late May 2020) & subsequent cumulative updates MUST be the reason, causes a major issue with Internet Explorer (as on 26th June 2020). Once you installed & removed a higher version of JRE, JRE 6.x will not load anymore on the browser. You can reset Internet Explorer, reset your JRE & what not? Trying to open the Oracle forms based application would show the plugin being loaded and that’s all!

Strangely, I can see this issue is with only JRE 6.x when JRE 7.x and later load as usual without any problems. So if you think you have tried “everything” already & landed on this page, let us give it another try.

You can divide the below tasks into two sections. If JRE 6.x is the only one java client remaining after removing all other versions & if the JAVA_HOME or other Java related keys are not set under environment variables, just delete the “Sun” & “Java” Folders from “AppData” “Roaming”, “LocalLow” & “Local” folders. You may delete the “Javasoft” entry from “Computer\HKEY_CURRENT_USER\SOFTWARE\JavaSoft” registry path also. Try to load Oracle forms based application and if the java client is NOT at all loading, follow the below instructions.

Uninstall Java Software

After uninstallation remove all remnants of Java software from the system.

This should open the appdata folder the current user.

Let us find and delete all “Java” and “Sun” folders and content within those folders. Please note, if you are using JAVA for other than accessing Oracle EBS, the following instructions may result disastrous situations. Hence I recommend you to continue with utter caution.

Delete “Sun” and “Java” folders as I have said before from “Roaming”, “LocalLow” & “Local”

Open RegEdit

and open the below path

Computer\HKEY_CURRENT_USER\SOFTWARE\JavaSoft

Delete the “JavaSoft” folder completely.

Now switch to “Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\JavaSoft”

This is the registry path where 32 Bit version of Java software is installed. Delete this folder also (if exists after uninstallation).

Now you are ready to re-install the Java client once again. I would prefer a computer restart prior attempting re-installation, which is not mandatory.

After the re-installation of JRE, opening the internet explorer should ask you for permission to enable the client and Oracle EBS should able to load the NPAPI client properly. Do let me know whether it helped you.

References:

Regards,

rajesh

Windows 10 network, action center, clock icons do not respond to mouse click

Hello guys

I started using Windows 10 from the early developer previews & was lucky until yesterday (24th June 2020). Few things didn’t work as expected then onwards.

I noticed the troubles when I clicked on the network icon on the taskbar, which usually pops up a window from which I can connect to another available connection or turn “Airplane Mode” on or off etc

Unfortunately, this has stopped on me all of a sudden. I started searching for a solution & came across few, which didn’t solve my issues. Today morning 25th June 2020, I noticed that it was not just the network connection icon, my clock and action center also had the same issue of not responding to mouse clicks!

  1. https://windowsreport.com/action-center-wont-open-windows-10/#:~:text=Open%20the%20Start%20Menu%20and,%3E%20Advanced%20options%20%3E%20Startup%20Settings.&text=If%20Action%20Center%20is%20working%2C%20restart%20your,and%20start%20Windows%2010%20normally.
  2. https://www.lifewire.com/fix-windows-10-action-center-wont-open-4588300

Please refer the above links for situation those may not be fixed by the below simple instruction. While trying to find a solution, accidently I landed on the below settings area & toggled the “Get notifications from apps and other senders” from on to off & then reversed it.

That was it! Toggling Notifications on and off has brought back the functionality to whole those icons which were not responding. Give it a try and let me know whether it helped you! (No need to restart the computer)

regards,

rajesh

FileZilla FTP Server with TLS

Hello guys

Today we will see how to setup FileZilla FTP server with TLS. Well there are many documents explaining it, however, as usual I am going one step ahead and explain few elements those are missed by most of those posts.

Once installed (accept the defaults, letting FileZilla run as a Windows service) you can start configuring it for PASSIVE mode transfers and TLS.

As you could see, the installation sets port 21 as default listening port for FTP. If you choose to, can change it an available port. Usually you don’t have to do it.

If your FTP server/desktop machine has multiple IP addresses, you may assign a particular IP for the FTP Server under “IP Bindings”

Leaving it to default will not hurt you, usually.

Setting up Passive Mode can be bit tricky, especially when you are going to let users from internet to access your FTP server. Let us consider you have a router that is the gateway for all your computers in the network. So any communication that is leaving your network passes through this router. This is called a NAT network. Getting a FTP server to respond to a request from internet could be quite confusing for someone who doesn’t understand the NAT properly (I don’t, all the time)

Usually such a requirement is handled by forwarding the requests received by the router to one of the servers or computers hosting the FTP or other services.

Here for FileZilla, the router should forward port 21 to the server that is hosting the FTP server, to that the communication and transfer could happen. So basically, your router should have an entry like below. With the given example, FTP Server software is installed on a computer that has STATIC IP address 192.168.1.200, hence the below entry. Please make sure that all your computers/servers those host services have STATIC IP addresses.

Please note, based on the manufacturer, Applications & Gaming tab will be labelled differently. You need to check the router manual to find out where exactly you can locate “Single Port Forwarding”. If you are lucky enough, you may able to pick FTP from an available dropdown list, like the one I have. Else, just create one entry like the one you could see with this image for FTP.

Now you need to configure the ports for Passive mode, which most of the modern FTP clients will try to use for the transfer. I believe, without configuring the passive mode, FileZilla FTP server fails to establish connections.

Here you could see that I am using a custom port range for Passive mode. Most of the people who successfully configured FileZilla recommend using the port range between 50000-51000. You can try the ranges like 50000-50100 or like the one I have, which is approximately a 1000 ports.

Next couple of points are going to be vital for users from internet. I have a static IP address from ISP, hence for the area External IP address, I can use it safely. However, this may not be the case of most home users, as ISP provide them dynamic IP addresses which keep on change once in a pre-planned interval. So http://www.noip.com is your friend and I suggest you create a ddns.net hostname for yourself before proceeding. You can use their client software to keep update your host IP address against ddns.net hostname. You can use your hostname, eg: johwick3.ddns.net in the place of external IP address.

OR

You can try the “Retrieve external IP address from” option as well. I cannot guarantee the success rate.

The other important setting on this page is “Don’t use External IP for local connections”. You must not uncheck this box in case if your FTP server has external IP configured and expecting both local and external users to access the server.

Once set, you need to open the range of ports on your router. This time you need to access the page “Port range forwarding” and create an entry like the seen below.

This completes the initial level settings for the FTP server. Let us configure TLS in the next step. This involves creating a self signed certificate and secret key (secret key is not mandatory). Luckily FileZilla has built-in ability to create a self-signed certificate for you!

Once you are on the FTP over TLS settings page, you can use generate new certificate button to bring this window up. Fill in correct information & generate the certificate. FileZilla will automatically setup the certificate for you. “Save key and certificate to this file:” should be chosen wisely. Your certificate is accessed from this location, so make sure that you will generate the certificate in a location/folder which is not accidently prone to get deleted.

Now we will create a new user & setup few details for the testing.

In the above step, I have created a user with read only privileges and assigned a single directory access. As long as you want your users NOT to upload files to your FTP server, this setup is sufficient for a normal user. Please refer FileZilla documents for in-depth knowledge about creating groups and users.

Finally, we will configure Windows Firewall in order to establish the connection. Please note, I am against disabling firewall and other security suites those are installed/configured on any environment, unless for testing. You must NOT disable them permanently. If your browsing and other internet activities are limited to reputed websites and services, I can insure that you don’t need a commercial product to protect your Windows box. The built in Defender suite does a wonderful job protecting your computer. Follow the below images to create an incoming rule for the FTP traffic in the very server/computer on with you have install FileZilla FTP server.

Please note, there is no need to open the port 990 that is used for TLS. Unfortunately while I was trying everything, included the port in my setup. You only need to allow TCP Ports 21 & the range 50000-51000 (or the port range that you prefer to use)

That’s all take for setup. We can try to connect to our FTP server now!

Download and install FileZilla FTP Client/Or one of the other FTP clients available (You may not succeed to connect to FTP server using Windows built in command line FTP, as it doesn’t support TLS)

As you could see from the quick connections area, I have connected to FTP server from both local and internet & both attempts were successful.

I hope, if you are desperately looking for FileZilla FTP server setup guidelines, this post helps.

regards,

rajesh