Introduction
This article is about an application which prints reports developed in SSRS 2008R2 using a Windows or web application. The program uses ReportExecutionService to print the report. The class is designed in a way that it is able to print multiple pages of reports in an SSRS 2008R2 environment. The program will give you an understanding of how we can construct a webrequest and build command to extract streams to print multiple pages.
Background
Our client has a Windows application which is used to manage their accounts and business transactions. We have developed SSRS Reporting Service 2005 for reporting purposes. They were able to view the invoicing reports and print those reports. After some time we got new requirement for bar code implementation. We need to show job number as a bar code in the report. In order to support the Barcode we used simple barcode font code 32. The problem with the SSRS 2005 is that it is not capable of embedding font in the text box. When we export the report in pdf the font is not getting exported. We are getting the number values instead of bar code.
We googled a lot and found out, to have this functionality we need to embed font in text box. SSRS 2005 report’s text box doesn’t have this property. When we search on internet we found that SSRS 2008R2 reporting Services supports for Rich Text box. We decided to give it a try.
We migrated our 2005 reports in 2008 reports it faced very few challenges after migration . We implemented the barcode Font in the reports. When we extracted the report in PDF it worked perfectly fine. We were very happy with our results. So we decided to run our basic test for printing functionality and the problem began.
Our old printing functionality was using ReportExecutionService for printing. We have used SreamID to get how many pages are available for printing. When we tried to print we were getting only one page to print. If there are multiple pages in the invoice document they are missed. If we export the report in PDF it is exporting perfectly.
Process
When we debugged the issue we found that the problem is with the ReportingService.Render
function’s StreamID. We used the reporting service Render
method to traverse through the pages as follows. If you look at the below code the streamIDs return the count of the pages. It gives you the numbers of stream contents in the report.
firstPage = rs.Render(format, deviceInfo, extension, encoding, mimeType, warnings, streamIDs)
_numberOfPages = streamIDs.Length + 1
pages = New Byte(_numberOfPages - 1)() {}
pages(0) = firstPage
Dim pageIndex As Integer = 1
Do While pageIndex < _numberOfPages
deviceInfo = String.Format("<DeviceInfo><OutputFormat>{0}</" & _
"OutputFormat><StartPage>{1}</StartPage></DeviceInfo>", "emf", pageIndex + 1)
pages(pageIndex) = rs.Render(format, deviceInfo, extension, encoding, mimeType, warnings, streamIDs)
pageIndex += 1
Loop
By setting up the deviceinfo with the following information we actually were able to get new pages:
"<DeviceInfo><OutputFormat>{0}</OutputFormat><StartPage>
{1}</StartPage></DeviceInfo>", "emf", pageIndex + 1
When we shifted to SSRS 2008R2, we were not able to do this because the StreamID returns empty array for some reason. We searched on the net and found out the reason behind this is the architectural changes; in SSRS 2008R2 as compared to SSRS 2005 the StreamId will always return an empty array. The reason behind is the on-demand page processing has been introduced in SSRS 2008R2.
Using the code
The below implemented code will give you the exact solution for printing multiple pages without streaming using the StreamId variable.
I am assuming that you have already added the ReportExecution2005.asmx as a web reference in your project for the 2005 SSRS Reporting Service.
Building Request
We are building an initial request in string. Using this request we are telling the server to render all the requests.
string requestUri = string.Format("{0}{1}{2}{3}&rs:Command=Render&rs:" & _
"Format=IMAGE&rc:OutputFormat=EMF", report.Url, "?", ReportPath, uRrlparamaters);
This is the command which helps to create the HTTP request. This request is to notify the user, process the current request, and also keep rendering the remaining pages.
//&rs:Command=Render&rs:Format=IMAGE&rc:OutputFormat=EMF
We have created a Web request using the above constructed string. In addition we are passing the parameter persistStreams
for the first page. Once we have the first page we will start asking for the next page until the pages are over.
CookieContainer cookies = new CookieContainer();
WebRequest request = WebRequest.Create(requestUri + persistStreams);
(request as HttpWebRequest).CookieContainer = cookies;
Now the below code will explain how to get all the pages. The while
loop we are using because we need to access all the pages of the SSRS reports. In this while
loop we are requesting pages till the last page. To execute a continuous request we need to provide authentication for the server. The SSRS server needs authentication for rendering the report. We need to provide authentication = true
for every request we make. The complete code can be found in my source code.
Once we create a request, we need to get a response from that request. We collect the response in a WebResponse
object. Once we get the response we copy the data in the byte array and add in the MemoryStream
for printing purpose. We also create a List
of MemoryStream
objects which can contain each page in its memory. If we get the page then we will add the page in to the list.
Once we add the page in the memory stream we ask for another page. If the memory stream is 0 then it will break the while
loop and you will have the collection of streams for printing.
while (true)
{
request.UseDefaultCredentials = true;
WebResponse response = request.GetResponse();
Stream s = null;
MemoryStream ms = new MemoryStream();
int count = 0;
byte[] data = null;
s = response.GetResponseStream();
data = new byte[1024];
do
{
count = s.Read(data, 0, data.Length);
ms.Write(data, 0, count);
} while (count > 0);
ms.Seek(0, SeekOrigin.Begin);
if (ms.Length == 0)
{
break;
}
m_streams.Add(ms);
request = WebRequest.Create(requestUri + getNextStream);
(request as HttpWebRequest).CookieContainer = cookies;
}
Points of Interest
Actually I have never used this type of page rendering for printing. The SSRS reports are hosted on the server and works as a website but we were dependent on the solution of the stream ID. This solution helped to resolve and upgrade our application to work like the previous one.