Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 

Printing Crystal Reports in .NET

created by Don Williams on July 29, 2015 3:16 PM


Printing Crystal Reports is a series of documents that explain how Crystal Reports behaves when viewing and printing using the various buttons and API’s. The series is authored by Don Williams and Ludek Uher, both Senior Support Engineers at AGS Product Support. The documents applies to Crystal Reports 2011 and above and SAP Crystal Reports, Developer Version for Visual Studio .NET only.



Crystal Reports uses printer drivers to construct its design pane. Due to this printer dependency, when reports are deployed to other computers, the print and view may not be as expected.  Understanding the concepts covered in this series of documents will help you achieve consistent printing and viewing output when deploying reports to different environments.


It is also highly recommended you review the Printer Doc’s in the link above to understand how and why CR uses Printers and how these are used in the SDK.




Basics – Loading the application

See Kbase article 2163438 to get the sample application I am using in this document and for recent updates.

(Note: Our new search engine currently does not allow attachments so I have attached the sample app to this Document. Unzip the file, Rename the attachment to


Requires Service Pack 14 or above which is available from here: Crystal Reports, Developer for Visual Studio


For standalone runtime, not using Visual Studio install the 32 bit MSI.


To get things going open the application attached in the above KBA and the Form1.cs file.

This application uses both the Engine ( ClientDocument ) and RAS ( Report Application Server -ReportClientDocument)


On application load it gets the localization to set the values to Imperial or Metric so it can calculate the number of twips etc. 1440 twips = 1 inch.

The next section queries the AppDomain to get the version of the CR Engine assembly which is installed with every CR .NET product. This confirms the version you are loading shown in Fig. 1 as the runtime version which is Service Pack 14 )

public frmMain()



            // Required for Windows Form Designer support




    if (System.Globalization.RegionInfo.CurrentRegion.IsMetric)

isMetric = 567;


isMetric = 1440;


    foreach (Assembly MyVerison in AppDomain.CurrentDomain.GetAssemblies())


        if (MyVerison.FullName.Substring(0, 38) == "CrystalDecisions.CrystalReports.Engine")


            //File: C:\Windows\assembly\GAC_MSIL\CrystalDecisions.CrystalReports.Engine\13.0.2000.0__692fbea5521e1304\CrystalDecisions.CrystalReports.Engine.dll

            //InternalName: Crystal Reports



            //FileDescription: Crystal Reports

            //Product: SBOP Crystal Reports


            //Debug: False

            //Patched: False

            //PreRelease: False

            //PrivateBuild: False

            //SpecialBuild: False

            //Language:        English (United States)


System.Diagnostics.FileVersionInfo fileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(MyVerison.Location);

txtRuntimeVersion.Text += fileVersionInfo.FileVersion.ToString();


CRVer = fileVersionInfo.FileVersion.Substring(0, 2);





    //End Class




            // TODO: Add any constructor code after InitializeComponent call





Now onto the main routines….

Loading the default printers:

The test applications main form looks like this:

Fig. 1



On application startup in the Form Load section there is the following code to query the registry to get the Users and Defaults list of installed printers as follows:

Note: This part is Windows System printer info


Current User vs Default user:

private void frmMain_Load(object sender, System.EventArgs e)


            if (System.Drawing.Printing.PrinterSettings.InstalledPrinters.Count > 0)


                        foreach(String myPrinter in System.Drawing.Printing.PrinterSettings.InstalledPrinters )




                        cboCurrentPrinters.SelectedIndex = 0;




                        rdoCurrent.Enabled = false;



            //For printers exposed to System account as per MS Kbase



            //Look to HKEY_USERS\.Default\Software\Microsoft\Windows NT\CurrentVersion\Devices

            Microsoft.Win32.RegistryKey mySystemPrinters =

                        Microsoft.Win32.Registry.Users.OpenSubKey(@".DEFAULT\Software\Microsoft\Windows NT\CurrentVersion\Devices");

            foreach (String defaultPrinters in mySystemPrinters.GetValueNames())




            if (cboDefaultPrinters.Items.Count > 0)


                        cboDefaultPrinters.SelectedIndex = 0;




                        rdoDefault.Enabled = false;



printerCurrentDuplexList.DataSource = System.Enum.GetValues(typeof(PrinterDuplex));



Which ever printers you have installed under your local PC User account will be listed under Current User.

The Default User list is limited, see the link above from MS on how to add the same User Printers under the Default Admin account in the registry.

Note, for WEB applications this is a must:;en-us;184291

Fig. 2

When a report is created the report will use the Users default printer driver installed on the development computer unless Page Setup is selected and the OK button is clicked. For more info how to design reports with nonspecific and specific printer requirements please see Printing Crystal Reports the Basics for more detail about this functionality.

The next 2 subroutines simply fill in the values selected from the above fig:








Two new API’s I’ve added to the Viewers default print button is to use the default PrintToPrinter (P2P) or the enhanced PrintOutputController (POC) Methods. P2P is the default and is limited in what it is capable of doing. See below in the ViewReport() routine for more info.


Fig. 3



The next section is to open the report:




And update various flags and enable buttons etc. Notice also the Engine opens the report and then the same report object is assigned to RAS and cloned just in case I need to use it:


rpt.Load(rptName.ToString(), OpenReportMethod.OpenReportByTempCopy);

// then clone the report, it may be needed later on

rpt1 = (CrystalDecisions.CrystalReports.Engine.ReportDocument)rpt.Clone();

// this assigns the report to RAS for modification

rptClientDoc = rpt.ReportClientDocument;

rptClientDoc1 = rpt.ReportClientDocument;


Now we get to the interesting parts….


Get the printer info as it is saved in the report




See the notes in the application also for more info.


Basically the Printer name is “Display” of no printer is selected in the RPT file. Enhancements to the SDK and CR Designer now save the Printer name, Paper size name, this is critical when using Custom paper sizes, Duplex settings and various other printer info when one is select. Again see the Printer Doc’s for more info on how and why this info is saved.


Note: Previous to SP 10 CR would simply use the paper size ENUM when selecting what paper size to use. SP 10 introduced an update to this property where now the engine will look for the Custom Paper Size by name. So when designing reports that use custom paper sizes be sure to create those exact same names on any work station or application Server so the Engine will find and automatically use the specified custom paper size by default. Any specific printer properties should also be configured on each work station.


To the code, as I was getting these enhancements integrated into the SDK I took the “long way” to evaluate what the report info has and how to display that info. So the code does look to be rather tedious but the logic is sound. I’m sure there is a better more dynamic way of doing the same and when I have time I will attempt to simplify this logic.


One note also is the Report file itself must be saved in a newer version of CR Designer so this info is saved in the RPT file, exporting to RPT format should also update the info but check it to make sure. It is a requirement now to use an updated RPT version.


For this reason I also had R&D expose the report history so you can now check if the report has been updated or not:


Fig. 4


In the above notice you now have the Report file version ( 14.0 ) as well as the number of Revisions and report save history. The save history can ONLY be updated in CR Designer standalone or from the built in Designer in Visual Studio.


Fig. 5



The saved report version history list is loaded into an array and Index 0 is the last saved version.


Next section is to close the report and reset the various flags and buttons.




Then if you want to save the report with the updated values.



This section simply resets all of the various flags and messages and disables buttons.



You can save the report with whatever changes you have made but sometimes requires you to export to RPT format so those changes are saved.


Now let’s update the report object with what is populated in the drop down list of printers:




In this routine it uses RAS to update the report printer info with what has been selected in the printer drop down list.


Note in the code the link to this KBA article explains how to set a No Printer Report to a selected printer report. If the link breaks it basically is a “how to” to create a dummy report with no printer and then clone the info and use it as the new printer setting:


After setting/updating the various properties it uses the PrintOutputController (POC) to update the RPT file:




Also notice one other thing I do and that is to set a flag to tell the viewer to use the RCD (rptClientDocument) object and not the RPT object.


IsRpt = false;


In the ViewReport() it checks and sets the viewer report source accordingly:

// Here's where you test if the report has been modified by RAS then set object accordingly



if (!IsRpt)


      crystalReportViewer1.ReportSource = rptClientDoc.ReportSource;




          crystalReportViewer1.ReportSource = rpt;



catch (Exception ex)


      btnSQLStatement.Text = "ERROR: " + ex.Message;



And then use the POC to print the report:




Or P2P to print the report




Notice also there are 2 ways to use P2P, I use the preferred way, it has a little more control.


Now if you want you can set the default Zoom mode:



Finally View the Report:


Here’s where you can set various viewer modes and properties, Group tree, Focus section and Export formats allowed:


// set up the format export types able to be used from Viewers Export button:

int myFOpts = (int)(

CrystalDecisions.Shared.ViewerExportFormats.RptFormat |

CrystalDecisions.Shared.ViewerExportFormats.PdfFormat |

CrystalDecisions.Shared.ViewerExportFormats.RptrFormat |

CrystalDecisions.Shared.ViewerExportFormats.XLSXFormat |

CrystalDecisions.Shared.ViewerExportFormats.CsvFormat |

CrystalDecisions.Shared.ViewerExportFormats.EditableRtfFormat |

CrystalDecisions.Shared.ViewerExportFormats.ExcelRecordFormat |

CrystalDecisions.Shared.ViewerExportFormats.RtfFormat |

CrystalDecisions.Shared.ViewerExportFormats.WordFormat |

CrystalDecisions.Shared.ViewerExportFormats.XmlFormat |

CrystalDecisions.Shared.ViewerExportFormats.ExcelFormat |


//CrystalDecisions.Shared.ViewerExportFormats.NoFormat); // no exports allowed

//int myFOpts = (int)(CrystalDecisions.Shared.ViewerExportFormats.AllFormats);


crystalReportViewer1.AllowedExportFormats = myFOpts;


One final subroutine is to get the section Orientation print modes. This also is the beginning on how to query each section to get various parts and values:



It can also be used as a starting point to get other report object info. As well as it does get the Section Paper Orientation to validate if this feature has been used or not.

See this KBA for the sample app:

More info on this product:

CR for VS no longer allows connecting to Business Objects Enterprise also known as Managed Reports. There is a separate .NET SDK for BOE 4.1 that eventually will have the above API’s but for now this is limited to CR for VS only. Although the Custom Paper size and using the ActiveX mode in BOE 4.1 .NET SDK should work.



Crystal Reports uses printer drivers to create the report design pane and to render the reports to screen, print and export. Printer drivers will affect the report behavior when the report is deployed to other computers and it is the report author's responsibility to choose the correct report initial conditions.



June 15, 2016 - Updated Subreport SQL, you will need to modify to set a value

Second up date is the No Printer function - if your report has no printer checked on and you want to print it to a landscape format you need to use the attached dummy reports and alter the page settings. Cloning the report allows the printer info to be used by RAS.

June 17, 2016 - fixed issue with NoPrinter orientation logic.