Printing SSRS 2008 R2 Reports from C#.

While working on a code for sending a SSRS report straight to the printer, ran into a interesting issue where the Render method was not giving back the StreamId's for the reports which had more than one page. This is known bug in SSRS 2008 R3 MS Link. To  get around this issue, you have to modify the element value in the element and call the Render method multiple times  in a Do while loop to get all the pages for the report.

The method RenderReport contains the code in which we iterate and call the Render method multiple times. Another bug, which i have not been able to resolve at the time of writing this post, is that the Fonts get somewhat strange when the report is published. Make sure that the you have the elements PrintDpiX and PrintDpiY present and thier values set to 96 in the element otherwise you might see the report printed out in oversize font.

To call the class PrintSSSRSReport use the code below

 PrintSSRSREport printReport = new PrintSSRSREport();
printReport.PrintReport(@"file://printerpath/printer", @"/FirstReport");
 

 public class PrintSSRSREport


{

ReportExecutionService reportExecutionService = new ReportExecutionService();

private byte[][] totalPages;

int numberOfPages = 0;

private int currentPrintingPage;

private int lastPrintingPage;



public PrintSSRSREport()

{

// Create proxy object and authenticate the SSRS reporting Service

reportExecutionService = new ReportExecutionService();

reportExecutionService.Credentials = System.Net.CredentialCache.DefaultCredentials;

reportExecutionService.Url = "http://servername/ReportServer/reportexecution2005.asmx";

}



public byte[][] RenderReport(string reportPath)

{

// Private variables for rendering

string deviceInfo = string.Empty;

string format = "IMAGE";

Byte[] result = null;

string extension;

string encoding;

string mimeType;

Warning[] warnings = null;

string[] streamIDs = null;

string historyID = null;

ParameterValue[] parameters = new ParameterValue[0];

List totalPages = new List();

bool pagesExist = true;

int pageCount = 1;

ExecutionInfo execInfo = new ExecutionInfo();

ExecutionHeader execHeader = new ExecutionHeader();



reportExecutionService.ExecutionHeaderValue = execHeader;

execInfo = reportExecutionService.LoadReport(reportPath, historyID);

reportExecutionService.SetExecutionParameters(parameters, "en-us");

String SessionId = reportExecutionService.ExecutionHeaderValue.ExecutionID;



//Exectute the report and get page count.

do

{

#region unused code

//using the PageWidth and PaageHEight was casuing only the first page to be returned

//deviceInfo = String.Format("" +

// " EMF" +

// " {0}cm" +

// " {1}cm" +

// " {2}cm" +

// " {3}cm" +

// " {4}cm" +

// " {5}cm9696" +

// " {6} " +

// "
", 96, 96, .1, .5, .5, .5, pageCount);

#endregion



deviceInfo = string.Format("EMF{0}9696",

pageCount);

result = reportExecutionService.Render(format, deviceInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);



//if there are no more pages then the length of the result will be 0, so get out of the do loop.

//this is being done because of the bug in SSRS 2008 R2

//MS URL for bug http://connect.microsoft.com/SQLServer/feedback/details/573997/with-ssrs-2008-r2-microsoft-reporting-winforms-serverreport-render-method-returns-no-stream-identifiers-for-image-format

pagesExist = (result.Length == 0) ? false : true;

execInfo = reportExecutionService.GetExecutionInfo();

pageCount++;



if (pagesExist)

{

totalPages.Add(result);

}



Console.WriteLine("Execution date and time: {0}", execInfo.ExecutionDateTime);





} while (pagesExist);



numberOfPages = totalPages.Count;

return totalPages.ToArray();

}



public bool PrintReport(string printerName, string reportName)

{

PrintDocument printDocument = null;

PrinterSettings printerSettings = null;



this.TotalPages = RenderReport(reportName);



try

{

// Wait for the report to completely render.

if (numberOfPages < 1)

return false;



printerSettings = new PrinterSettings();

printerSettings.MaximumPage = numberOfPages;

printerSettings.MinimumPage = 1;

printerSettings.PrintRange = PrintRange.SomePages;

printerSettings.FromPage = 1;

printerSettings.ToPage = numberOfPages;

printerSettings.PrinterName = printerName;



printDocument = new PrintDocument();

currentPrintingPage = 1;

lastPrintingPage = numberOfPages;

printDocument.PrinterSettings = printerSettings;



// Print report

printDocument.PrintPage += new PrintPageEventHandler(this.printDocument_PrintPage);

printDocument.Print();



}

catch

{

//Console.WriteLine(ex.Message);

throw;

}

finally

{

if (printDocument != null)

{

printDocument.Dispose();

}



}



return true;

}



private void printDocument_PrintPage(object sender, PrintPageEventArgs ev)

{

ev.HasMorePages = false;



using (MemoryStream stream = new MemoryStream(TotalPages[currentPrintingPage - 1]))

{

stream.Position = 0;



using (Metafile page = new Metafile(stream))

{

ev.Graphics.DrawImage(page, 0, 0, page.Width, page.Height);

}

}



ev.HasMorePages = (++currentPrintingPage <= lastPrintingPage) ? true : false;

}



public byte[][] TotalPages

{

get

{

return totalPages;

}

set

{

totalPages = value;

}

}

}

Comments

  1. Hello Jamali, thanks for share this tip, I'm having the same issue here, I've copied your code, but I have a little question: Which Namespace should I use for this Object Declaration:

    List totalPages = new List();

    Visual Studio is proposing me:
    Microsoft.ReportingServices.ReportRendering

    but if I use this namespace I'm getting an error because the constructor of this class is internal.


    Thank you Very much In Advance!

    Fernando Sosa
    Mendoza, Argentina.

    ReplyDelete
  2. Thank you so much for the sharing. I'm troubled by SSRS print for a long time and this is the best stuff I found.

    ReplyDelete
  3. @Fernando --> that is a normal List object that is used to contain byte array returned from this line of code result = reportExecutionService.Render(format, deviceInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);

    ReplyDelete
  4. @Chunxing --> You are most welcome

    ReplyDelete

Post a Comment

Popular posts from this blog

Print from WPF using ReportViewer Control

Using IOptions class in .Net Core Unit Testing.