Welcome to MSDN Blogs Sign in | Join | Help

Report Viewer at PDC 2009

Stella Chan, Program Manager for the Reporting Services developer scenarios, will be presenting at PDC this week.  If you're attending the conference, check out her session: Developing Rich Reporting Solutions with Microsoft SQL Server 2008 R2.  She will demo lots of new Reporting Services functionality, including the new Visual Studio 2010 ReportViewer control.
Posted by Brian Hartman | 3 Comments
Filed under: ,

JavaScript API

One of the new features we added to the ASP.Net Report Viewer in Visual Studio 2010 is a JavaScript API to allow you to interact with the viewer on client.  In reading many of the posts on the report controls forum, we found that many people struggle when implementing a custom toolbar or replacing portions of the toolbar functionality.  The new JavaScript API is intended to make it easier for you to provide the same functionality available through the built-in toolbar with a minimum amount of effort.

The JavaScript API is exposed through the client side ReportViewer object.  Specifically, it’s the Microsoft.Reporting.WebFormsClient.ReportViewer class.  An instance of this class is created on the client for each instance of the ReportViewer control on the page.

Referencing the client side viewer

The ReportViewer client side object inherits from Sys.UI.Control.  To obtain a reference to the client side viewer, use the $find method as follows:

    var clientViewer = $find("ReportViewer1");

The identifier you pass to $find corresponds to the ClientID of the ReportViewer server control.

Checking the state of the viewer

Once you have a reference to the client side viewer, you will want to check the state of the viewer before invoking most of the various methods and properties it exposes.  When the viewer is in a loading state, most of the functionality of the client viewer is unavailable and will throw an exception if called.  The viewer is loading whenever an asynchronous postback is in progress.  It is also in the loading state while retrieving a report page.  This usually happens during an asynchronous postback, but can also extend beyond the lifetime of the postback, such as while retrieving images displayed on the report page.  To check the state of the viewer, use the isLoading property:

    var isLoading = clientViewer.get_isLoading();

Once you have determined that the viewer is no longer loading new data, you can determine what is being displayed using the reportAreaContentType property.  It will indicate if the viewer is blank, displaying a report, or displaying an error message:

    if (!isLoading)
    {
        var reportAreaContentType = clientViewer.get_reportAreaContentType();
        if (reportAreaContentType ===
            Microsoft.Reporting.WebFormsClient.ReportAreaContent.ReportPage)
        {    
            // Perform various operations
        }
    }

Both the isLoading and the reportAreaContentType properties as well as all of the other properties on the client viewer fire the propertyChanged event.  Listening to that event can be useful for determining when you should enable or disable your custom UI.

Client side operations

The client side report viewer supports a number of operations, similar to those available with the built-in toolbar.  MSDN has a list of public properties and methods available on the client side report viewer.  It includes everything from invoking the print control and exporting a report to setting the zoom and scroll position of the report.  The built-in toolbar uses the public JavaScript API to perform its operations, so invoking the exportReport method on the client viewer will look exactly the same as selecting an export format from the built-in dropdown.

I have also included a sample with the article which demonstrates some of the new JavaScript APIs.  The sample was written with Visual Studio 2010 Beta 2.

Visual Studio 2010 Beta 2 is Now Available

Visual Studio 2010 Beta 2 is now available for download!  This beta includes plenty of new features, but it is particularly noteworthy for the ReportViewer control.  Beta 2 contains several key updates, including:

ASP.Net AJAX - The report viewer will now use AJAX internally to update its own content.  It can also be placed inside an ASP.Net AJAX UpdatePanel.

RDLC 2008 – The report viewer can now consume RDLC in the SQL Server 2008 RDL schema.  Local mode now supports the same RDL features that are currently available in SQL Server 2008, including tablix, chart, gauge, and rich text.

Improved browser support – The ReportViewer control supports IE 6, 7, and 8 (in quirks and standards mode for all versions) as well as Firefox 3.5 and Safari 4.0.

I’ll be posting additional information and tutorials on these and other new features over the next few weeks.  As always, please let us know of any feedback you have.  We’re still working on a few more features and bug fixes for this release (particularly in the browser support area), so we definitely want to hear about any problems or issues you run into with the beta.

Posted by Brian Hartman | 0 Comments
Filed under:

GDI+ Updated Again

Last year, I wrote about Microsoft Update 956391, a security update to GDI+ that the Report Viewer client print control depends on.  For security reasons, that update forced us to enable a kill bit for the client print control.  A new security update is being released for GDI+ today via Microsoft Update MS09-062 (KB 957488).  But this time, we’ve taken some additional steps this time to hopefully provide a less impactful and smoother transition.

Why we include GDI+ in the first place

The client print control receives EMF files from the report server or report viewer control and uses GDI+ to display them in print preview or to send the data to the printer.  Reporting Services and the ReportViewer control are supported on a number of operating systems, going back to Windows 2000.  While gdiplus.dll has shipped with newer Windows operating systems, it did not ship with Windows 2000. 

Previous releases of Reporting Services have included GDI+ in the ActiveX control package to ensure that the control works across all supported operating systems without any additional installation by the end user.  Printing on Windows 2003, XP, or any newer operating system uses the GDI+ assembly that ships with the operating system, ignoring the one deployed with the ActiveX control.

What we changed this time

Beginning with SQL Server Reporting Services 2008, we stopped shipping GDI+ with the print control.  So SSRS 2008 is not affected by this update.  Reporting Services 2000 and 2005, as well as the Visual Studio 2008 Report Viewer control all ship the print control with GDI+ so they are being updated.  The report viewer that ship with VS 2005 is also being updated so that it can connect to an updated report server.
Like the previous update, this change modifies the CLSID associated with the print control.  The following table shows the affected values:

Version CLSID
Originally shipped CLSID {FA91DF8D-53AB-455D-AB20-F2F023E498D3}
CLSID after previous update {41861299-EAB2-4DCC-986C-802AE12AC499}
New CLSID {0D221D00-A6ED-477C-8A91-41F3B660A832}

Our GDI+ dependency going forward

The print control will continue to require GDI+ going forward.  But due to the obvious impact to our customers as well as the number of different products we need to update during any GDI+ security bulletin, we will no longer be shipping gdiplus.dll with any version of the print control.  That is, this patched version of the ActiveX print control does not include the updated version of gdiplus.dll.  As a result, any future changes to GDI+ will not require an update to any reporting services product.

Most of our users should see no impact to this because the print control is using gdiplus.dll from the operating system and not the one deployed with the ActiveX download.  But from this point forward, browsers running on Windows 2000 may need to install GDI+ separately.  While it’s not ideal to require end users to perform a separate installation, we feel this provides the best experience to the majority of our customers in the case of future security updates.  And in many cases, GDI+ will already have been installed on Windows 2000 machines via the .Net Framework or other such installation.

The transition

As with the previous update, we will be issuing the IE kill bit for the old print control.  But because the GDI+ assembly included with the print control is only used on Windows 2000 machines, only that operating system will receive the kill bit.  All other operating systems can continue to use the existing print control without any changes.  The kill bit is not being issued until February 2010, in order to provide you with sufficient time to update your environments.  Once the kill bit is issued, Windows 2000 machines connecting to unpatched reporting services installations will receive the “Unable to load client print control” error message when attempting to print.

What you need to do

It’s the client operating system, not the one running the report server, that determines whether you will need to update anything.  If you don’t expect to have any clients running Windows 2000, you don’t have to update anything.  But if you do and you are using one of the products in the “What we updated this time” section above, then you will need to patch your report server and report viewer.

SQL Server 2000 SP2: http://www.microsoft.com/downloads/details.aspx?familyid=33554f96-5af7-4683-a537-9db293b67b8d

SQL Server 2005 SP2: http://www.microsoft.com/downloads/details.aspx?familyid=d971a262-1dfb-498c-a4f3-59fdc1b85d23

SQL Server 2005 SP3: http://www.microsoft.com/downloads/details.aspx?familyid=0d878f4b-71e8-4170-9a14-1bce684811ce

Visual Studio 2005 SP1: http://www.microsoft.com/downloads/details.aspx?familyid=e186aeed-e9d7-4a02-84b3-bbed116ca060

Visual Studio 2008: http://www.microsoft.com/downloads/details.aspx?familyid=4fa10c93-ce20-43df-a725-ef4c77353747

Visual Studio 2008 SP1: http://www.microsoft.com/downloads/details.aspx?familyid=b904dee8-8a26-43f8-8ca9-86ad12cfdb52

Report Viewer 2005 SP1 Redistributable: http://www.microsoft.com/downloads/details.aspx?familyid=0dfaf300-2b53-4678-a779-0d805ddfe538

Report Viewer 2008 Redistributable: http://www.microsoft.com/downloads/details.aspx?familyid=42ed040f-cf94-4754-b0b3-c8016fbcbe22

Report Viewer 2008 SP1 Redistributable: http://www.microsoft.com/downloads/details.aspx?familyid=6aaa74bd-a46e-4478-b4e1-2063d18d2d42

GDI+ Standalone Installation: http://www.microsoft.com/downloads/details.aspx?FamilyId=6A63AB9C-DF12-4D41-933C-BE590FEAA05A

SQL Server 2008 R2 August CTP is Available

The SQL Server 2008 R2 August CTP is now available for download.  Reporting Services has a number of new features available in this CTP which are being discussed on the Reporting Services team blog.

This is the first release of SQL Server to include portions of the ASP.Net AJAX work we have done for the report viewer.  SQL Server doesn’t ship with a standalone ReportViewer control, so you won’t be able to use it in Visual Studio just yet.  But you can get a preview of the updated UI, some of our usability enhancements, and the more fluid page navigation and report interactivity experience.  The standalone ReportViewer control will be available in Visual Studio 2010 Beta 2.

Some of the new UI features you will see in this release:

  • The buttons to collapse the prompt area and document map have been replaced with splitter bars, similar to the UI we have been using in the report viewer webpart.
  • Multi-value parameters are now resizable!
  • The export drop down is now a drop down menu, taking up less space on the toolbar
  • All page navigation and report interactivity is done with AJAX.  You will no longer need to press the back button repeatedly in the browser to return from a drillthrough report.
  • Report interactivity features such as search and toggle will no longer reposition the report (i.e. adjust the report scroll position unexpectedly).  The viewer will maintain the scroll position of the report to the extent possible.

As always, we want to hear your feedback.

Where’s the new Report Viewer?

Previously, I posted that we were working on an updated version of the report viewer control for Visual Studio 2010.  With beta 1 now available, I have naturally started receiving questions wondering why the report viewer that is included in beta 1 is essentially the same thing that shipped with Visual Studio 2008.

I want assure everyone that our plans have not changed.  We will ship an updated report viewer in VS 2010.  Unfortunately, the timing just didn’t work out for us to get this update included in beta 1.  Our current plan is for the updated viewer to appear in beta 2.

I’ll be talking a lot more about the new report viewer features once beta 2 is available.  But I do want to at least provide an overview of the work we’re doing.  Below are some of the new features or changes we’re making.  This is certainly not an exhaustive list.

  • Support for the 2008 RDL schema in local mode.  This will give you all of the new features available in RDL in SQL Server 2008, including tablix, rich text, updated chart visualizations, gauge, and many others.  We’ll be including the updated report design surface for local mode as well.
  • Support for ASP.Net AJAX.  The report viewer will use AJAX to update its various regions (report, toolbar, etc).  You will also allow be able to include the entire ReportViewer control in an UpdatePanel.
  • Updated API.  We’ve received a lot of feedback on missing APIs or scenarios that are difficult given the current API.  We’ve added a number of new events and methods to help, including a new JavaScript API for interacting with the ReportViewer in the browser.
  • Significantly improved browser compatibility.  We’ve put a huge amount of effort into improving our support across browsers.  We’ve seen plenty of reports of extra scrollbars or other problematic renderings in Firefox, Safari, and standards mode in general.  The initial feedback on these changes has been very positive.
  • Usability and “look and feel” enhancements.  The viewer needed a minor facelift.  It got one.

As always, if there is a particular feature or problem you’d like us to address, we want to know.  Please send feedback via Microsoft Connect.  Our team reviews that feedback weekly.

Posted by Brian Hartman | 2 Comments
Filed under:

Manually Printing a Report

Even though the ReportViewer has built-in print functionality, people often wish to implement their own version to provide significant customizations or a deeper integration with their application.  There are a number of samples out on the web, but I thought it might be useful to provide one here since the request is so common.  Attached to this post is a sample PrintDocument that will programmatically print a given ServerReport or LocalReport.

Generating a report for printing uses the image renderer, which is a hard page break renderer.  Unlike viewing a report within the report viewer UI, a hard page break renderer must cut off a page at a specific size.  In the report viewer UI, a page can be enlarged to make a table or chart fit.  You can’t do that with paper.

As a result, calculating the content for an arbitrary page can be very expensive.  When the image renderer receives a request to generate page 10, for example, it internally generates pages 1 through 9 and throws them out.  So asking for one page at a time from the image renderer will be prohibitively slow, even for small reports.  To address this performance issue, both local and server mode have the ability to request all the pages at once.

In local mode, the sample uses the Render overload that takes a CreateStreamCallback.  This causes the renderer to keep generating pages until it hits the end of the report.  The callback is invoked once for each page to generate a stream to store the image for that page.

Server mode is a little different - the server can’t ask the client for a callback for each page.  Instead, the report server supports the concept of persisted streams.  The URL access parameter rs:PersistStreams is used to tell the server to generate all of the pages and to persist those pages on the server.  This request returns the first page.  Each subsequent page can be retrieved using the rs:GetNextStream parameter.  When you try to go beyond the last page, the server returns an empty response.

This sample is just a starting point to demonstrate the ReportViewer APIs.  There are a number of ways it can be improved.  Given that both rendering a page and sending it to the printer can be long running operations, you might consider rendering the pages in a background thread, allowing you to continue generating pages while sending completed pages to the printer.  You also might wish to extend it to use printers other than the default printer.  Or you could extend OnBeginPrint to handle printing a specific range of pages rather than all of them.  Or you could use file system backed streams, since using MemoryStreams could consume a lot of memory on a large report.

Posted by Brian Hartman | 1 Comments
Filed under: ,

Attachment(s): ReportPrintDocument.cs

Did Your Session Really Expire?

The ASP.Net ReportViewer relies on ASP.Net session state to store critical data that can’t always be easily regenerated.  In local mode, it stores the report snapshot, which contains a compiled form of the report definition, some or all of the data used in the report, and some report state information such as which toggles have been expanded.  In server mode, depending on your configuration, session state may be used to store connection information to the report server.  The report viewer stores data in session state when the ASPX page is executing and retrieves it during future postbacks or from the report viewer’s HTTP handler when rendering the report asynchronously, retrieving report images, exporting the report, or printing.

The report viewer will throw an AspNetSessionExpiredException when information it previously placed in session is not available during a postback or HTTP handler operation.  The exception message is "ASP.NET session has expired".  But this can be misleading.  Fundamentally, the exception means that the expected data wasn’t in session state.  But this can be for a number of different reasons:

1. Your session really did expire
The report viewer will connect to its HTTP handler one minute prior to session expiring as long as the web page is still being displayed in the browser.  This keeps session from expiring while the report is still being viewed.  It tends to work well, but it isn’t a guarantee.  The web server may be under load or the connection may not be reliable.  If the user has been inactive for a long time and this "ping" does fail, the session will expire and future requests will fail with this exception.  In this situation, there generally isn’t much a user can do other than refresh the page to start report processing over again.

2. The session isn’t available to the server handling the request
I often see session state configured incorrectly when load balancing the web front end across multiple machines or a web garden.  If your load balancing doesn’t have client affinity, you’ll need to make sure that session state is shared across all nodes.  ASP.Net session defaults to inproc mode, meaning that it is stored in memory in a single process.  A separate process in a web garden or a separate machine handling a request to render the report will have its own instance of session state that does not contain the information the report viewer stored while executing the page.  This tends to manifest as a seemingly random failure - some images or charts will fail to load or the report will fail to render non-deterministically.  The requests that get routed to the process that handled the original ASPX request succeed but requests routed elsewhere fail.  The solution to this problem is to use a session state mode that can be shared by all nodes in the farm.

3. Cookies are disabled on the client
ASP.Net keeps track of a session with an HTTP cookie.  If a client has cookies disabled, postbacks or requests to the report viewer HTTP handler will not load the existing session, resulting in this exception.  In this case, you can turn on cookieless session mode.  ASP.Net will redirect the first request to the ASPX page to a URL that contains a session identifier rather than storing the identifier in a cookie.

Posted by Brian Hartman | 1 Comments
Filed under: ,

Why is ReportViewer Ignoring BindingSource Operations?

I’ve seen several posts in the Report Viewer forum from frustrated users who are trying to use a BindingSource as a data source to the ReportViewer in local mode.  Problems typically manifest when setting sorting or filtering options on the BindingSource object, but those operations are not reflected in your report.  So what’s going on?

The short answer is that it’s a bug in the ReportViewer.  When presented with a BindingSource, the viewer is reaching in and referencing the underlying data source, which bypasses any transformations the BindingSource applies.  So instead of displaying filtered data, the report viewer still shows you the raw data in the report.

We’ve fixed this bug for the next release of the viewer.  But until that is released, there’s an easy way to code around this problem.  Since the viewer uses the underlying data source rather than the transformed output, adding an extra BindingSource layer fixes the problem.  In you form load event, you can add the following code:

BindingSource tempBindingSource = new BindingSource(this.OriginalBindingSource, "");
this.reportViewer1.LocalReport.DataSources[0].Value = tempBindingSource;

You will need to replace "OriginalBindingSource" and "reportViewer1" with the equivalent names of objects on your form.  The index into the DataSources collection may also be different, depending on your report.

With this workaround, when the ReportViewer processes the report, it sees the tempBindingSource as the data source.  Our bug causes the viewer to reference the underlying data source of tempBindingSource, which is the filtered output from the original BindingSource object.  As a result, you get the expected data in the report.

On a side note, I want to mention that this bug was first reported to us via Microsoft Connect.  If you believe you are running into a bug in the report viewer or have a request for a new feature, Connect is a great way to contact us.  We track these issues directly and the site allows us to communicate back and forth if we have trouble reproducing the problem.  It also allows you to vote on existing feature requests or bugs to help us prioritize the work.

Posted by Brian Hartman | 2 Comments
Filed under: ,

Report Viewer in Visual Web Developer 2008 Express

After the initial release of the ReportViewer ASP.Net control with Visual Studio 2005, we also released a separate add-on to the Visual Web Developer 2005 Express sku.  But when Visual Studio 2008 was released, there was no equivalent add-in for the web developer sku.  I’m happy to announce that this add-in is now available for Visual Web Developer 2008 Express Edition.

The ReportViewer control included in this download is the same one that is available with the other Visual Studio 2008 skus.  This does not add new functionality to the design time or run time experience beyond making it available in a new sku.

English, French, and Japanese versions are available now.  Additional languages will be available soon.

Download Details: http://www.microsoft.com/downloads/details.aspx?FamilyID=b67b9445-c206-4ff7-8716-a8129370fa1d

Posted by Brian Hartman | 0 Comments
Filed under: ,

SQL Server 2008 and the ReportViewer Controls

In August, we released SQL Server 2008 with a number of new features for Reporting Services.  There have been questions lately about how this impacts the report viewer controls.  The answer depends on whether you use server mode or local mode.

Server Mode

We spent a great deal of time making sure that the existing report viewer controls (both the Visual Studio 2005 and 2008 versions) are capable to rendering reports from a SQL Server 2008 Report Server.  If you publish a report to the server (in any of the SQL 2000, 2005, or 2008 definition schemas), then point a report viewer control to that report, it will render it.  There are, however, two limitations to viewing any report from the 2008 server in the existing viewer controls.

One of the new features of the report processing engine is that it now evaluates the report on demand.  To the extent possible, the processing engine will only perform the calculations necessary to return the report page that has been requested by the client.  A side effect of this approach is that the processing engine does not always know the actual page count of the report when rendering any given page.  We added a new SOAP API to accommodate this so that clients can detect this condition and display an appropriate UI.  But the existing report viewer controls use the old SOAP API.  The old API always returns the actual page count, maintaining backwards compatibility.  But this necessitates performing additional calculations and therefore does not take advantage of all of the performance improvements we made in SQL Server 2008.  Other processing engine performance improvements are still used, so you will see benefits regardless of this limitation.

The second new feature that impacts the report viewer is the use of rich text.  The new rich textbox allows you to specify multiple formats and actions in a single RDL textbox.  But due to limitations in the rendering format used by the existing ReportViewer winforms control, only the first format in a rich textbox will be applied when displaying the report through the winforms viewer.  Additionally, report actions (drillthoughs, bookmarks, and hyperlinks) will be ignored if they are applied to individual text runs rather than the textbox as a whole.  These limitations do not apply to the ASP.Net report viewer.

Aside from these two limitations, all other new features of the report definition will work through the existing report viewer controls in server mode.  You can use the new charts, gauges, tablix, and the new Word renderer for exporting, all without any updates to your client.

Local Mode

Local mode is a different story.  When using local mode with the VS 2005 or VS 2008 viewer controls, you are using the same report processing engine that was shipped with SQL Server 2005.  This engine does not understand the new report definition schema and attempting to load a report created with one of the new SQL Server 2008 authoring tools will result in this error:

The report definition is not valid. Details: The report definition has an invalid target namespace 'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition' which cannot be upgraded.

Looking Forward 

We are working on updated versions of both the winforms and ASP.Net report viewer controls to support the new report processing engine in local mode and therefore all of the new RDL features as well (new charts, tablix, rich text, etc).  This upgrade should also remove the limitations described above for server mode.  The current plan is to release the updated viewer controls with Visual Studio 2010.  We are also adding lots of new features to the viewers, which I will be talking about more in the coming months.  Disclaimer: Please be aware that while this is our current plan, it is always possible that plans change along the way.

Custom Credentials in the Report Viewer

When using the ReportViewer control in server mode, you will need to consider how you want to authenticate to the report server.  By default, the viewer connects as the current thread user by supplying CredentialCache.DefaultCredentials to its underlying WebRequest.  One of the questions I am frequently asked is why there isn’t a simple credentials property on the ReportViewer.  That is, why can’t you simply write reportViewer1.ServerReport.Credentials = new NetworkCredential(…)?  Let’s take a look at that question and the best practices around it.

IReportServerCredentials

When you are using the ASP.Net version of the ReportViewer control, supplying custom credentials means you need to implement IReportServerCredentials.  An instance of this interface is then typically passed to ServerReport.ReportServerCredentials.  The interface is defined as:

public interface IReportServerCredentials
{
    WindowsIdentity ImpersonationUser { get; }
    ICredentials NetworkCredentials { get; }
    bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority);
}

IReportServerCredentials allows you three different ways to control the authentication to the report server.  These methods are not mutually exclusive, so you can use any combination of them if needed.

ImpersonationUser defines a WindowsIdentity that the ReportViewer will impersonate before each WebRequest call to the server.  In effect, the viewer will call WindowsIdentity.Impersonate on the identity returned from this property, make the call to the server, then revert the thread identity immediately afterwards.  If you return null from this method, the server call is made as the current thread user.

The value of the NetworkCredentials property is passed directly to the WebRequest object via the WebRequest.Credentials property.  As noted earlier, if you return null for this property, the viewer will default to CredentialCache.DefaultCredentials.

The final method, GetFormsCredentials, is the only report server specific authentication mechanism on the interface.  If you return true from this method, the out parameters are used to call LogonUser on the report server.  This method is designed for use with custom security extensions installed on the report server.

This still doesn’t explain why we have the interface at all.  These properties and methods could have easily been included on the ServerReport object.  So why have the interface?  That comes down to when the report viewer uses the credentials.

With the ASP.Net report viewer, calls to the report server don't just happen when the ASPX page is executing.  There are a number of times when the browser calls back to the web server via the ReportViewer HTTP handler.  The most common case is to retrieve images in the report.  Images are retrieved after the HTML for the report page has been sent to the client.  When the viewer receives a request for an image, it must relay that request to the report server to get the image.  That report server request must be authenticated as the same user that ran the report initially.  Other "out of band" cases include printing and exporting the report.

There are two ways an HTTP handler can get information: via the incoming request or from stored state on the web server.  Sending credentials on the request URL would require sending the credentials to the client, which is an obvious security problem.  So the viewer opts to use stored state on the web server.  The instance of this interface that you give to the viewer is placed in ASP.Net SessionState when the ASPX page is executing so that it can be retrieved by the HTTP handler later.

Storing credentials anywhere is something that should be done with caution.  By using an interface, we can provide you with a great deal of flexibility over what actually gets stored in SessionState.  Unfortunately, the most common implementation I see for this interface tends to look something like this:

[Serializable]
class MyCredentials : IReportServerCredentials
{
    private string m_userName;
    private string m_password;

    public MyCredentials(string userName, string password)
    {
        m_userName = userName;
        m_password = password;
    }

    public WindowsIdentity ImpersonationUser
    {
        get { return null; }
    }

    public ICredentials NetworkCredentials
    {
        get { return new NetworkCredential(m_userName, m_password); }
    }

    public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
    {
        authCookie = null;
        userName = null;
        password = null;
        authority = null;
    }
}

In this case, you are storing the credentials directly in SessionState.  While not necessarily insecure, there is often a more secure way to do it.  Let’s say you always read the user name and password from the web.config file.  In that case, you could read the credentials directly from the config file from within your implementation.  This version accomplishes the same goal but without storing the actual credentials in SessionState:

[Serializable]
class MyConfigFileCredentials : IReportServerCredentials
{
    public MyConfigFileCredentials()
    {
    }
 
    public WindowsIdentity ImpersonationUser
    {
        get { return null; }
    }
 
    public ICredentials NetworkCredentials
    {
        get
        {
            return new NetworkCredential(
                ConfigurationManager.AppSettings["MyUserName"],
                ConfigurationManager.AppSettings["MyPassword"]);
        }
    }
 
    public bool GetFormsCredentials(out Cookie authCookie, out string userName, out string password, out string authority)
    {
        authCookie = null;
        userName = null;
        password = null;
        authority = null;
    }
}

One additional point to note is that because this class is being stored in SessionState, it must be marked as serializable if your application sets the SessionState mode to anything other than inproc.

What if you don’t want to use session?

There are plenty of cases when you don’t want SessionState enabled at all in your application.  To handle this scenario, there is a second way to supply credentials for server mode.  You can implement IReportServerConnection or IReportServerConnection2 instead:

public interface IReportServerConnection : IReportServerCredentials
{
    Uri ReportServerUrl { get; }
    int Timeout { get; }
}

IReportServerConnection derives from IReportServerCredentials.  So everything noted above is equally applicable here.  In addition to credentials, you supply the report server url and timeout for the server.  This information was being stored in SessionState too.  But with SessionState disabled, it needs to come from your object instead.  In order to make your object available to the report viewer HTTP handler without storing it, you must register your class in your web.config file:

<configuration>
    <appSettings>
        <add key="ReportViewerServerConnection" value="MyNamespace.MyServerConnectionClass, MyAssembly"/>
    </appSettings>
</configuration>

The viewer instantiates your object each time it needs connection information, either while executing the ASPX page or the HTTP handler.  With this approach, you shouldn’t set the ServerReport.ReportServerCredentials property at all.  Using the web.config file can be also beneficial even if you have SessionState available to you because it prevents the need to place an instance of your object in SessionState for each active session that has accessed the viewer.  The tradeoff to this approach is that you only get one config file setting.  The same IReportServerConnection implementation will be used for all report viewers anywhere on your site.  You can use any HttpContext information you want to return different values in different situations, but you will need to code that into the one registered type.

What about winforms?

With all of this talk about HTTP handlers and SessionState, we should also touch on how credentials work with the winforms report viewer.  Since everything is sitting in memory on the client all of the time, there is no need to implement any interfaces.  The ServerReport.ReportServerCredentials property in the winforms namespace doesn’t have a set accessor.  Instead, it provides an implementation that includes set accessors for each of the authentication properties and methods.  So you can set the ImpersonationUser or any other authentication property directly on the ServerReport.ReportServerCredentials object.

Client Print Fails to Load After Microsoft Update 956391

A number of people have reported problems using the ActiveX print control in the report viewer after installing Microsoft Update 956391.  Specifically, users receive the error "Unable to load client print control" when clicking on the print icon in the report viewer toolbar.  I would like to explain what is happening, why we did it, and how to fix the problems that you or your customers are experiencing.

What’s a Kill Bit?

Let’s start with what Microsoft Update 956391 actually does.  This update includes a kill bit for the ActiveX client print control used by Reporting Services.  This print control is distributed with a number of components, including SQL Server 2000, SQL Server 2005, and Visual Studio 2008.  A kill bit is an instruction to Internet Explorer to prevent it from loading an ActiveX control.  In this case, Internet Explorer has been told not to load the Reporting Services client print control.  As a result, when attempting to do so, the report viewer fails and displays the "unable to load" error.

Setting the kill bit is typically done only in extreme cases, such as security vulnerabilities.  That’s what happened in this case.  The security vulnerability is not actually in the print control itself, but rather the GDI+ DLL that we ship with the print control.  The details of that vulnerability are described in MS08-052.  Even though we have patched the vulnerability, the kill bit is a defense in depth option to prevent malicious users from sending the Microsoft signed client print control and then exploiting the vulnerability.  The kill bit prevents the client print control from loading regardless of where it comes from.

Which Client Print Controls are Affected?

The client print controls that shipped with SQL Server 2000, SQL Server 2005, Visual Studio 2008, and the Report Viewer 2008 redistributable all use the same CLSID and are therefore all disabled by the update.  The table below shows the different CLSIDs that are used by the different versions of Reporting Services:

Product Client (IE) Architecture CLSID
SQL 2000, SQL 2005, VS 2008 (old) X86 {FA91DF8D-53AB-455D-AB20-F2F023E498D3}
SQL 2000, SQL 2005, VS 2008 (new) X86 {41861299-EAB2-4DCC-986C-802AE12AC499}
SQL 2008 X86 {5554DCB0-700B-498D-9B58-4E40E5814405}
SQL 2008 X64, IA64 {60677965-AB8B-464f-9B04-4BA871A2F17F}

The previously shipped versions (using the first CLSID in the table) have been updated with a new version of GDI+ and a new CLSID to allow IE to load the control.  If you are using Report Server 2000, Report Server 2005, or the web control from VS 2008 in local mode, you will need to download an update so that your servers distribute the new print control.

Notice that SQL Server 2008 is not affected by this update and uses a different CLSID.  The print control that ships with SQL Server 2008 does not include the GDI+ DLL.  This DLL was included in earlier versions due to the requirement that they work on Windows 98 and Windows 2000.  But with that requirement dropped from SQL Server 2008, we no longer need to ship GDI+.  The new CLSID for SQL Server 2008 prevents Internet Explorer from replacing a working client print control on Windows 98 or 2000 with the newer version that does not work on that operating system.

Which Report Viewers are Affected?

Now that we’ve established which print controls are affected, we also need to talk about the report viewer control itself.  Even though the ReportViewer control in Visual Studio 2005 does not ship a print control, it still needs to be updated.  To understand why, let’s take a look at the HTML that is sent to the browser when you click on the print button:

<OBJECT ID="RSClientPrint" CLASSID="CLSID:FA91DF8D-53AB-455D-AB20-F2F023E498D3" CODEBASE="/ReportServer/Reserved.ReportViewerWebControl.axd?<report specific values>&amp;OpType=PrintCab#Version=2005,090,3042,00" VIEWASTEXT></OBJECT>

The object tag specifically references the disabled CLSID.  Even though an updated report server will return a new client print control, the ReportViewer web control still generates the old CLSID.  In hindsight, it would have been better for the Report Server to expose the CLSID for its client print control and for the ReportViewer ASP.Net control to use that when generating its HTML.  But those products have already shipped.  So we have also updated all versions of the ReportViewer (including the ReportViewer Web Part for use in SharePoint integrated mode) to write out the new CLSID.

We also support using the client print control directly in your application without a ReportViewer control.  If you do that, you will need to update it to write out the new CLSID as well.

What do you need to download?

To get to a working state, you will need to do the following:

  1. Update your report server to distribute the updated client print control.
  2. Update your ReportViewer redistributable so that it references the new CLSID.  The ReportViewer 2008 redistributable also includes an updated client print control for use in local mode.
  3. Update Visual Studio.  This is for the same reason as the redistributable.  The ReportViewer is installed with Visual Studio for design time.
  4. Update your Reporting Services SharePoint Add-In if you are using the report server in SharePoint integrated mode.

Below is a list of available updates (also available from MS08-052):

SQL Server 2000: http://www.microsoft.com/downloads/details.aspx?familyid=5F9E7F78-7439-414B-A9DC-A779B89427DB

SQL Server 2005: http://www.microsoft.com/downloads/details.aspx?familyid=5148B887-F323-4ADB-9721-61E1C0CFD213

Visual Studio 2005: http://www.microsoft.com/downloads/details.aspx?familyid=A7BF790B-3249-4EE8-9440-FA911EBBC08A

Visual Studio 2008: http://www.microsoft.com/downloads/details.aspx?familyid=A8C80B29-6D00-4949-A005-5D706122919A

Report Viewer 2005 Redistributable: http://www.microsoft.com/downloads/details.aspx?familyid=82833F27-081D-4B72-83EF-2836360A904D

Report Viewer 2008 Redistributable: http://www.microsoft.com/downloads/details.aspx?familyid=6AE0AA19-3E6C-474C-9D57-05B2347456B1

Reporting Services SharePoint Add-In: http://www.microsoft.com/downloads/details.aspx?FamilyID=1e53f882-0c16-4847-b331-132274ae8c84&displaylang=en

Welcome

Welcome to my blog.  My name is Brian Hartman.  I'm a lead developer on the SQL Server Reporting Services team.  I've been a member of this team since the first release of Reporting Services and have worked in several areas of the product.  For the past four years, my primary responsibility has been the ReportViewer controls.  I've been thinking about starting a blog for some time now, but like many people, kept putting it off due to other demands on my time.  Recent encouragement from others has finally given this effort the priority it deserves.

I have spent a few years answering questions in the report controls forum.  In doing so, some recurring issues have surfaced and the need for a single site that can serve as a collection of report viewer related solutions has become apparent.  My intention is to update this blog regularly with code samples, in depth explanations of ReportViewer behavior, and news on upcoming releases.  I hope you find it valuable.

 
Page view tracker