Click here to Skip to main content
Click here to Skip to main content

Adding DOC, RTF, and OOXML Export Formats to the Microsoft Report Viewer Control

By , 31 Jul 2008
 

Introduction

The Microsoft Report Viewer 2005 control does not support exporting to Microsoft Word formats, by default, but following the steps outlined in this article, you will be able to get the Report Viewer to generate reports in Microsoft Word formats (DOC, RTF, WordprocessingML, and OOXML) when working in the local mode.

I came across this issue while working on a project for one of my clients recently. The project initially relied on using the Microsoft Report Viewer, but later, the client realized they needed reports in the DOC format. I have found this was possible when using Microsoft SQL Server 2005 Reporting Services with a third-party tool, Aspose.Words for Reporting Services, but the client insisted that SQL Server should not be used, even the free SQL Server Express Edition was rejected by the client.

During the evaluation of SQL Server Reporting Services with Aspose.Words for Reporting Services, I thoroughly enjoyed the reports as Microsoft Word documents, and was very disappointed that the same was not possible in the Report Viewer. Out of curiosity, I did some digging, and would like to share my findings.

Understand it

The Microsoft Report Viewer is a powerful .NET control allowing to embed RDL and RDLC reports in WinForms and ASP.NET applications. It enables users to view and export reports to different formats such as PDF or HTML. The control is included with Microsoft Visual Studio 2005, and it is also available as a free download from Microsoft.

The Report Viewer can generate reports independently using a built-in engine (which is called local mode), or it can display reports that are generated on a Microsoft SQL Server 2005 Reporting Services Report Server (remote mode).

When working in the remote mode, the Report Viewer is able to export reports to all formats installed on the Report Server it connects to. It means, the list of export formats available in the control dropdown is exactly the same as the one displayed in the Report Manager dropdown. The default rendering formats available in the remote mode are: Excel, MHTML, PDF, TIFF, XML, and CSV. The important thing here is that the list of export formats is expandable by installing custom rendering extensions on SQL Server. There are very useful third-party rendering extensions available on the market that allow exporting of reports to Microsoft Word formats (DOC, RTF, OOXML), for example Aspose.Words for Reporting Services.

However, when working in the local mode, the list of export formats is limited to only a few formats, and cannot be expanded because the export is hard coded into the Report Viewer assemblies. This fact represents a serious disadvantage as many users would like to have the ability to export reports to DOC, RTF, OOXML, and other formats when using Report Viewer in the local mode.

This article describes a way of overcoming this limitation and making Report Viewer generate reports in Microsoft Word document formats. Our goal is to add a custom rendering extension to the standard Microsoft Report Viewer control. To achieve that, we will use several widely known .NET tools to modify the Report Viewer assemblies.

Essentially, what is described here is “white-box hacking” or “ethical hacking”. White-box means, you follow simple transparent steps yourself to produce a hacked version, and you can be sure there are no unwanted surprises in it. Ethical means you are not making any harm or annoyance to anyone.

The techniques described in this article will involve:

  • Using Reflector to peek into assembly files.
  • Using Microsoft’s ILASM And ILDASM tools to disassemble and assemble intermediate code.
  • Using Microsoft’s ILMerge tool to merge assemblies.

There is also a way of implementing the same thing by making much less effort. This method was suggested by the community member Bravo Niner (thank you so much for that!) after the article was published. The idea is that you can use private Reflection instead of hacking the Report Viewer control. You still have to hack the rendering extension you want to integrate with the control (unless it was developed by you, of course), but most of the annoying steps can still be omitted. This technique is described in the Adding Export Formats Dynamically Using Private Reflection section at the end of the article.

Disclaimer

Read and use at your own risk. The author only expresses his personal views, and does not endorse or promote steps described in this article.

Investigate it

To get started, you need to have Microsoft Report Viewer 2005 installed on your computer. You can either select it as a feature during the Microsoft Visual Studio 2005 installation, or you can download the Microsoft Report Viewer 2005 Redistributable from the Microsoft website.

Let’s investigate how it works and what we want to get in the end. The Report Viewer control consists of several assemblies installed to the Global Assembly Cache (GAC). Here is the list:

  • Microsoft.ReportViewer.Common – contains classes common for both WinForms and ASP.NET controls.
  • Microsoft.ReportViewer.ProcessingObjectModel – contains classes responsible for local report processing (similar to those used by Reporting Services).
  • Microsoft.ReportViewer.WinForms – contains classes specific for WinForms controls.
  • Microsoft.ReportViewer.Design – contains designer classes for WinForms controls.
  • Microsoft.ReportViewer.WebForms – contains classes specific for ASP.NET controls.
  • Microsoft.ReportViewer.WebDesign – contains designer classes for ASP.NET controls.

The first step we should accomplish is to extract the assemblies from the GAC. This may seem simple, but unfortunately, we can’t do that using Windows Explorer. When viewing the GAC folder, it invokes a special Shell extension that prevents assemblies from doing simple copy/paste. So, we are going to use the command line to complete this task.

Assuming your working directory is C:\Work, run the following commands from the console:

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.common\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.common.dll c:\work

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.processingobjectmodel\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.processingobjectmodel.dll c:\work

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.winforms\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.winforms.dll c:\work

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.design\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.design.dll c:\work

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.webforms\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.webforms.dll c:\work

copy c:\windows\assembly\gac_msil\microsoft.reportviewer.webdesign\
8.0.0.0__b03f5f7f11d50a3a\microsoft.reportviewer.webdesign.dll c:\work

You can now see six DLLs in the C:\Work folder:

image1.png

Next, download the excellent Reflector tool by Lutz Roeder (unless you already have it on your machine… almost no doubt you do) and open the Microsoft.ReportViewer.Common.dll assembly. Locate the Microsoft.Reporting.ControlService.ListRenderingExtensions method. The disassembled method looks like the following:

image2.png

public override IEnumerable<LocalRenderingExtensionInfo> ListRenderingExtensions()
{ 
    if (this.m_renderingExtensions == null) 
    { 
        List<LocalRenderingExtensionInfo> list = new List<LocalRenderingExtensionInfo>(); 

        Html40RenderingExtension extension = new Html40RenderingExtension(); 
        list.Add(new LocalRenderingExtensionInfo("HTML4.0", extension.LocalizedName, false, 
            typeof(Html40RenderingExtension), false)); 

        Microsoft.ReportingServices.Rendering.ExcelRenderer.ExcelRenderer renderer = 
          new Microsoft.ReportingServices.Rendering.ExcelRenderer.ExcelRenderer(); 
        list.Add(new LocalRenderingExtensionInfo("Excel", renderer.LocalizedName, true, 
          typeof(Microsoft.ReportingServices.Rendering.ExcelRenderer.ExcelRenderer), true));

        RemoteGdiReport report = new RemoteGdiReport(); 
        list.Add(new LocalRenderingExtensionInfo("RGDI", report.LocalizedName, false,  
            typeof(RemoteGdiReport), false));

        ImageReport report2 = new ImageReport(); 
        list.Add(new LocalRenderingExtensionInfo("IMAGE", report2.LocalizedName, false, 
            typeof(ImageReport), true));

        PdfReport report3 = new PdfReport(); 
        list.Add(new LocalRenderingExtensionInfo("PDF", report3.LocalizedName, true, 
            typeof(PdfReport), true));

        this.m_renderingExtensions = list; 
    } 

    return this.m_renderingExtensions; 
}

As you can see, the method returns a generic list of LocalRenderingExtensionInfo objects, each of them containing information about a certain rendering extension. You can see that the list of export formats is hardcoded, and new formats cannot be added using a configuration file like on the Report Server.

You can also notice that each rendering extension class derives from the RenderingExtensionBase class which, in turn, implements the IExtension and IRenderingExtension interfaces. It is an interesting fact, because these interfaces and rendering extension classes look very similar to the ones used in the full-fledged and extensible Microsoft SQL Server 2005 Reporting Services.

Let’s make a hypothesis that Microsoft actually used the same code for rendering extensions on the server and in the viewer control, but just packaged it differently. They’ve made it possible to add new custom rendering extensions on the server, but disabled doing so on the client by hard-coding the list of export formats. We can endlessly argue about Microsoft’s reasons for doing this, but this is outside of the scope of this article.

For our purposes, it is enough to know that in Report Viewer, the rendering extensions are located in the Microsoft.ReportViewer.XXX assemblies, and on the Reporting Server, they are located in the corresponding Microsoft.ReportingServices.XXX assemblies.

The consequence of our theory is that if we somehow add a custom rendering extension that works on the Reporting Server to the ListRenderingExtensions method in the Report Viewer, it will work, and Report Viewer will be able to generate reports in more formats.

We are going to take a popular commercial product Aspose.Words for Reporting Services that allows Microsoft SQL Server Reporting Services to export reports to Microsoft Word document formats (DOC, DOCX, RTF, and WordprocessingML). We are going to add Aspose.Words for Reporting Services to the Report Viewer’s list of rendering extensions so exporting to Microsoft Word formats is available in the control too.

You need to download an evaluation version of Aspose.Words for Reporting Services from its download page and place the \Bin\SSRS2005\Aspose.Words.ReportingServices.dll assembly in C:\Work. Leverage Reflector again, and you will notice that almost the whole assembly is obfuscated, except for four public classes, each of which represents a rendering extension for a specific format:

image3.png

That is exactly what we need. Our ultimate goal is to add the information about these extensions to the list returned by the ListRenderingExtensions method using the existing pattern. The code to add should look like the following:

DocRenderer docRenderer = new DocRenderer ();
list.Add(new LocalRenderingExtensionInfo("AWDOC", 
    docRenderer.LocalizedName, false, typeof(DocRenderer), false)); 

DocxRenderer docxRenderer = new DocxRenderer ();
list.Add(new LocalRenderingExtensionInfo("AWDOCX", 
    docxRenderer.LocalizedName, false, typeof(DocxRenderer), false)); 

RtfRenderer rtfRenderer = new RtfRenderer ();
list.Add(new LocalRenderingExtensionInfo("AWRTF", 
    rtfRenderer.LocalizedName, false, typeof(RtfRenderer), false)); 

WordMLRenderer wordMLRenderer = new WordMLRenderer ();
list.Add(new LocalRenderingExtensionInfo("AWWORDML", 
    wordMLRenderer.LocalizedName, false, typeof(WordMLRenderer), false));

Now, let’s do that.

Implement it

Step 1: Merge ReportViewer assemblies

The first step is merging the extracted Report Viewer assemblies into one. Why would we need that? The answer is: necessity and convenience.

First of all, let’s think about what we are going to do next. We plan to hack assemblies by modifying their IL code; therefore, we will be forced to remove their strong names and public keys. This means we will have to update all references to those assemblies as well, and don’t forget we have six Report Viewer assemblies + a rendering extension assembly to work with.

Second, Microsoft.ReportViewer.Common.dll is marked with a pair of InternalsVisibleTo attributes, allowing access to its internal members to code from the Microsoft.ReportViewer.WinForms.dll and Microsoft.ReportViewer.WebForms.dll assemblies. That means we will need to update the assembly name passed to the attribute’s constructor; moreover, this attribute does not seem to work with unsigned assemblies at all.

So, let’s make it a single assembly, and get rid of all the problems at once.

However, we can’t merge all six assemblies straightaway, because the designer assemblies contain classes with the same fully qualified names. Besides, you would hardly need to have both the WinForms and ASP.NET controls in one large assembly. You can create two assemblies if needed. In this article, we will be working with the WinForms controls, so the assemblies you need to focus on are:

  • Microsoft.ReportViewer.Common.dll
  • Microsoft.ReportViewer.WinForms.dll
  • Microsoft.ReportViewer.Design.dll

To merge assemblies, download and install a simple utility by Microsoft named ILMerge. It is available here.

Change the current directory to C:\Work, and run the following command from the console:

"c:\program files\microsoft\ilmerge\ilmerge" 
    /out:Microsoft.ReportViewer.WinForms.Modified.dll
    microsoft.reportviewer.common.dll
    microsoft.reportviewer.processingobjectmodel.dll
    microsoft.reportviewer.winforms.dll
    microsoft.reportviewer.design.dll

Now, we have the single Microsoft.ReportViewer.WinForms.Modified.dll assembly containing all the necessary Report Viewer classes in one place:

image4.png

Step 2: Disassemble

Now, we are ready to extract some IL code from our assemblies. We need to alter both Report Viewer and the rendering extension assemblies, so let’s disassemble both. We are going to use ILDASM.exe, which is a disassembler included with the .NET Framework SDK.

Launch the Visual Studio 2005 command prompt, and launch the following commands:

ildasm Microsoft.ReportViewer.WinForms.Modified.dll 
    /out=Microsoft.ReportViewer.WinForms.Modified.il

ildasm Aspose.Words.ReportingServices.dll 
    /out=Aspose.Words.ReportingServices.il /unicode

Step 3: Remove public keys

Since we are going to modify the IL code, we have to get rid of the public keys as we won’t be able to reassemble the code then. However, if you explore Microsoft.ReportViewer.WinForms.Modified.dll, you will find it is not strong named. This is because ILMerge builds an unsigned assembly by default, unless you explicitly instruct it to sign the target assembly.

Okay, but we still have Aspose.Words.ReportingServices.dll signed, so we need to remove the public key from the IL code. Open Aspose.Words.ReportingServices.il in Notepad or any other text editor (it might take some time to load because it is around 37 MB in size). Search for “.publickey =”, select as shown below, and press Delete:

image5.png

Step 4: Modify references in the rendering extension

Our next goal is to redirect the rendering extension assembly’s references so that it refers to the modified Report Viewer assembly instead of Microsoft.ReportingServices.Interfaces.dll and Microsoft.ReportingServices.ProcessingCore.dll.

Locate those references in Aspose.Words.ReportingServices.il and remove them:

image6.png

image7.png

Now, add a reference to Microsoft.ReportViewer.WinForms.Modified.dll:

.assembly extern Microsoft.ReportViewer.WinForms.Modified
{
}

image8.png

We are not done yet though. As you might know, identifiers in IL are always fully qualified, and are preceded with [assembly_name] where assembly_name is the name of the assembly containing the referenced object. Hence, we have to replace all these references with new assembly names. Press Ctrl-H and replace all occurrences of the [Microsoft.ReportingServices.Interfaces] and [Microsoft.ReportingServices.ProcessingCore] strings with [Microsoft.ReportViewer.WinForms.Modified].

image9.png

Step 5: Remove mangled resources

There is a small hassle specific to the Aspose.Words for Reporting Services’ obfuscation, or at least, to the version I worked with (2.0.2.0). The obfuscation tool they use mangles the names of some embedded resources, and for some weird reason, ILASM fails to assemble the code, although I was sure it should accept any Unicode name.

Anyway, the easiest way I found out is to merely get rid of those resources (they seem to be some test harness leftovers).

Locate where the embedded resources are specified (a bunch of .mresource tokens) and simply delete all those that have unreadable Unicode names (such as Ӕ.ӗ.resources). I located four such mangled names for version 2.0.2.0 of the rendering extension.

image10.png

Save and close Aspose.Words.ReportingServices.il.

Step 6: Modify the references in Report Viewer

We are almost ready to add the information about custom rendering extensions (and therefore additional export formats), but first, we need to add a reference to the rendering extension assembly.

Open Microsoft.ReportViewer.WinForms.Modified.il and add the reference:

.assembly extern Aspose.Words.ReportingServices
{
}

image11.png

Step 7A (for both WinForms and WebForms controls): Modify the ListRenderingExtensions method

We are ready to alter the Microsoft.Reporting.ControlService.ListRenderingExtensions method by adding LocalRenderingExtensionInfo objects that contain the information about additional rendering extensions. Basically, we can notice how this addition is implemented in IL and copy/paste this block of code and change it in a few places. Here is the modified method that adds the Aspose.Words for Reporting Services renderers (the changes are highlighted):

.method public hidebysig virtual instance class 
    [mscorlib]System.Collections.Generic.IEnumerable`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo> 
    ListRenderingExtensions() cil managed 
{ 
// Code size 403 (0x193) 
.maxstack 41 
.locals init (class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo> V_0, 
class Microsoft.ReportingServices.Rendering.HtmlRenderer.Html40RenderingExtension V_1, 
class Microsoft.ReportingServices.Rendering.ExcelRenderer.ExcelRenderer V_2,

// --- ADDED CODE
 
class [Aspose.Words.ReportingServices]Aspose.Words.ReportingServices.DocRenderer V_3, 
class [Aspose.Words.ReportingServices]Aspose.Words.ReportingServices.RtfRenderer V_4, 
class [Aspose.Words.ReportingServices]Aspose.Words.ReportingServices.WordMLRenderer V_5, 
class [Aspose.Words.ReportingServices]Aspose.Words.ReportingServices.DocxRenderer V_6,

// --- END OF ADDED CODE
 
class Microsoft.ReportingServices.Rendering.ImageRenderer.RemoteGdiReport V_7, 
class Microsoft.ReportingServices.Rendering.ImageRenderer.ImageReport V_8, 
class Microsoft.ReportingServices.Rendering.ImageRenderer.PdfReport V_9) 
IL_0000: ldarg.0 
IL_0001: ldfld class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo> 
    Microsoft.Reporting.ControlService::m_renderingExtensions 
IL_0006: brtrue IL_018c 
IL_000b: newobj instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::.ctor() 
IL_0010: stloc.0 
IL_0011: newobj instance void Microsoft.ReportingServices.Rendering.
    HtmlRenderer.Html40RenderingExtension::.ctor() 
IL_0016: stloc.1 
IL_0017: ldloc.0 
IL_0018: ldstr "HTML4.0" 
IL_001d: ldloc.1 
IL_001e: callvirt instance string Microsoft.ReportingServices.Rendering.
    HtmlRenderer.RenderingExtensionBase::get_LocalizedName() 
IL_0023: ldc.i4.0 
IL_0024: ldtoken Microsoft.ReportingServices.Rendering.HtmlRenderer.
    Html40RenderingExtension 
IL_0029: call class [mscorlib]System.Type 
    [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
IL_002e: ldc.i4.0 
IL_002f: newobj instance void Microsoft.Reporting.
  LocalRenderingExtensionInfo::.ctor(string, string, bool, class [mscorlib]System.Type, bool) 
IL_0034: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::Add(!0) 

...

// --- ADDED CODE

IL_0061: newobj instance void [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocRenderer::.ctor() 
IL_0066: stloc.3 
IL_0067: ldloc.0 
IL_0068: ldstr "AWDOC" 
IL_006d: ldloc.3 
IL_006e: callvirt instance string [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocRenderer::get_LocalizedName() 
IL_0073: ldc.i4.1 
IL_0074: ldtoken [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocRenderer 
IL_0079: call class [mscorlib]System.Type 
    [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
IL_007e: ldc.i4.1 
IL_007f: newobj instance void Microsoft.Reporting.
  LocalRenderingExtensionInfo::.ctor(string, string, bool, class [mscorlib]System.Type, bool) 
IL_0084: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::Add(!0) 
IL_0089: newobj instance void [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.RtfRenderer::.ctor() 
IL_008e: stloc.s V_4 
IL_0090: ldloc.0 
IL_0091: ldstr "AWRTF" 
IL_0096: ldloc.s V_4 
IL_0098: callvirt instance string [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.RtfRenderer::get_LocalizedName() 
IL_009d: ldc.i4.1 
IL_009e: ldtoken [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.RtfRenderer 
IL_00a3: call class [mscorlib]System.Type 
    [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
IL_00a8: ldc.i4.1 
IL_00a9: newobj instance void Microsoft.Reporting.
  LocalRenderingExtensionInfo::.ctor(string, string, bool, class [mscorlib]System.Type, bool) 
IL_00ae: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::Add(!0) 
IL_00b3: newobj instance void [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.WordMLRenderer::.ctor() 
IL_00b8: stloc.s V_5 
IL_00ba: ldloc.0 
IL_00bb: ldstr "AWWML" 
IL_00c0: ldloc.s V_5 
IL_00c2: callvirt instance string [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.WordMLRenderer::get_LocalizedName() 
IL_00c7: ldc.i4.1 
IL_00c8: ldtoken [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.WordMLRenderer 
IL_00cd: call class [mscorlib]System.Type 
    [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
IL_00d2: ldc.i4.1 
IL_00d3: newobj instance void Microsoft.Reporting.
  LocalRenderingExtensionInfo::.ctor(string, string, bool, class [mscorlib]System.Type, bool) 
IL_00d8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::Add(!0) 
IL_00dd: newobj instance void [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocxRenderer::.ctor() 
IL_00e2: stloc.s V_6 
IL_00e4: ldloc.0 
IL_00e5: ldstr "AWDOCX" 
IL_00ea: ldloc.s V_6 
IL_00ec: callvirt instance string [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocxRenderer::get_LocalizedName() 
IL_00f1: ldc.i4.1 
IL_00f2: ldtoken [Aspose.Words.ReportingServices]
    Aspose.Words.ReportingServices.DocxRenderer 
IL_00f7: call class [mscorlib]System.Type 
    [mscorlib]System.Type::GetTypeFromHandle(valuetype 
                     [mscorlib]System.RuntimeTypeHandle) 
IL_00fc: ldc.i4.1 
IL_00fd: newobj instance void Microsoft.Reporting.
    LocalRenderingExtensionInfo::.ctor(string, string, bool, 
               class [mscorlib]System.Type, bool) 
IL_0102: callvirt instance void class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo>::Add(!0) 

// --- END OF ADDED CODE

...

IL_0185: ldarg.0 
IL_0186: ldloc.0 
IL_0187: stfld class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo> 
       Microsoft.Reporting.ControlService::m_renderingExtensions 
IL_018c: ldarg.0 
IL_018d: ldfld class [mscorlib]System.Collections.Generic.List`1
    <class Microsoft.Reporting.LocalRenderingExtensionInfo> 
       Microsoft.Reporting.ControlService::m_renderingExtensions 
IL_0192: ret 
} // end of method ControlService::ListRenderingExtensions

Replace your ListRenderingExtensions method with the above listing, and proceed to the next step.

Step 7B (for WebForms control only): Modify the HTTP Handler type

We have to apply a bit more changes in order to get the WebForms control working. Since Microsoft.ReportViewer.WebForms.Modified.dll has no public key anymore, all HTTP handler type references should now point to this unsigned assembly. Here are two places in Microsoft.ReportViewer.WebForms.Modified.il you should additionally modify:

  1. Locate the following method:
  2. .method assembly hidebysig static bool 
      ConfigContainsHandler(class [System.Configuration]
           System.Configuration.Configuration config)

    and change the HTTP handler reference from:

          IL_0064:  ldstr      "Microsoft.Reporting.WebForms.HttpHandler, Microsof"
          + "t.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyTok"
          + "en=b03f5f7f11d50a3a"

    to

          IL_0064:  ldstr      "Microsoft.Reporting.WebForms.HttpHandler, Microsof"
          + "t.ReportViewer.WebForms.Modified"
  3. Locate the class:
  4. .class private abstract auto ansi sealed 
           beforefieldinit Microsoft.Reporting.WebForms.Constants
           extends [mscorlib]System.Object

    and replace the following field:

      .field public static literal string HttpHandlerTypeName = 
         "Microsoft.Reporting.WebForms.HttpHandler, Microsof"
         + "t.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, 
                                     PublicKeyToken=b03f5f7f11d50a3a"

    to

      .field public static literal string HttpHandlerTypeName = 
      "Microsoft.Reporting.WebForms.HttpHandler, Microsof"
      + "t.ReportViewer.WebForms.Modified"

    Notice, you'll then have to modify the web.config accordingly: replace the original type references:

    ...
    <httpHandlers>
    
    <add path="Reserved.ReportViewerWebControl.axd" verb="*" 
    type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, 
          Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    
    validate="false" />
    
    </httpHandlers>
    ...
    <buildProviders>
    
    <add extension=".rdlc" 
    type="Microsoft.Reporting.RdlBuildProvider, Microsoft.ReportViewer.Common, 
          Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    
    </buildProviders>
    
    ...

    with:

    ...
    <httpHandlers>
    
    <add path="Reserved.ReportViewerWebControl.axd" verb="*" 
      type="Microsoft.Reporting.WebForms.HttpHandler, 
            Microsoft.ReportViewer.WebForms.Modified"
    
    validate="false" />
    
    </httpHandlers>
    ...
    <buildProviders>
    
    <add extension=".rdlc" 
      type="Microsoft.Reporting.RdlBuildProvider, 
            Microsoft.ReportViewer.WebForms.Modified />
    
    </buildProviders>
    
    ...

Step 8: Modify designer reference

The Report Viewer control uses a designer that adds some custom actions to the standard Visual Studio designer. As you might know, a designer is tied to a control by marking it with DesignerAttribute. The Microsoft.Reporting.WinForms.ReportViewer class (which is the control itself) is marked with this attribute as seen in Reflector:

[Designer("Microsoft.Reporting.WinForms.ReportViewerDesigner, 
    Microsoft.ReportViewer.Design, Version=8.0.0.0, 
    Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(IDesigner))]

Note that the first parameter of the attribute constructor is a string representing the name of the designer class and the name of the containing assembly, separated by a comma. Remember, we have merged all Report Viewer assemblies including the designer assembly into one, so this reference becomes invalid, and the designer will fail to load when dropping the control onto a form. To get it working, we have to specify that the designer class is now located in the same assembly. The simplest way of doing that is merely using the overload of the DesignerAttribute constructor that accepts Type and passes typeof(Microsoft.Reporting.WinForms.ReportViewerDesigner). Assuming the designer class is not external anymore, the modified attribute looks as follows in IL:

.custom instance void [System]System.ComponentModel.
    DesignerAttribute::.ctor(class [mscorlib]System.Type) = (
01 00 31 4D 69 63 72 6F 73 6F 66 74 2E 52 65 70 // ..1Microsoft.Rep 
6F 72 74 69 6E 67 2E 57 69 6E 46 6F 72 6D 73 2E // orting.WinForms. 
52 65 70 6F 72 74 56 69 65 77 65 72 44 65 73 69 // ReportViewerDesi 
67 6E 65 72 00 00 ) // gner..

Search for DesignerAttribute (there should be only one occurrence over the whole file) and replace it with the above code.

image12.png

Save and close Microsoft.ReportViewer.WinForms.il.

Step 9: Assemble

We are almost there! All we have to do is assemble our modified IL code.

Run the following commands from the Visual Studio command prompt while in the C:\Work folder:

ilasm Microsoft.ReportViewer.WinForms.Modified.il /dll

ilasm Aspose.Words.ReportingServices.il /dll

Step 10: Test new export formats

It’s time to add our modified control to Visual Studio 2005. Launch the IDE, create a Windows Application project, right click on the Toolbox, and select Choose items. Now, browse to C:\Work and select Microsoft.ReportViewer.WinForms.Modified.dll.

image13.png

The modified Report Viewer control should appear in the current Toolbox group. Drag and drop it onto a form, and finalize your application.

Notice that although Microsoft.ReportViewer.WinForms.Modified.dll is automatically copied to the output directory by default, it won’t happen to Aspose.Words.ReportingServices.dll, and you will have to copy it manually. If you wish to overcome this problem, you can choose between at least two options I believe:

  • Resign the assemblies with your own public key and install them to GAC. Note, you have to update the references accordingly when editing the IL code of the assemblies.
  • Merge the two assemblies into one large assembly.

After the modified Report Viewer is added to your application, you will notice that the appearance, behavior, and other properties of the control remain absolutely similar to those of the original one, and the only difference is a bunch of new formats appearing in the export formats dropdown:

image14.png

Congratulations, you are now able to export your reports to Microsoft Word formats in the Report Viewer control!

Adding export formats dynamically using private Reflection

After the article was published, one of its readers (Bravo Niner) suggested a great idea that helps simplify the process dramatically. Actually, there is a much easier way of achieving the goal - why not just use private Reflection instead of hacking the control? That way, you can inject a rendering extension into the list dynamically, whenever you need. Of course, if you use a third-party rendering extension (such as Aspose.Words for Reporting Services), you still need to modify it so that it refers to the Microsoft.ReportViewer.Common.dll assembly, where the IRenderingExtension interface is defined. That means, you still have to:

  1. Disassemble Aspose.Words.ReportingServices.dll to IL (see Step 2).
  2. Remove the public key (see Step 3).
  3. Modify references (see Step 4, but note this time, you need to add a reference to Microsoft.ReportViewer.Common.dll as we don't merge/hack/do anything else to the Report Viewer assemblies).
  4. Remove mangled resources (see Step 5).
  5. Assemble the IL back to Aspose.Words.ReportingServices.dll (see Step 9).

However, if you create your own rendering extension from scratch, you don't need to hack anything at all; just make sure it implements the IRenderingExtension interface located in the Microsoft.ReportViewer.Common.dll assembly.

After you have your rendering extension ready, implement the following method:

private static void AddExtension(ReportViewer viewer, string name, Type extensionType)
{
    const BindingFlags Flags = BindingFlags.NonPublic | 
                               BindingFlags.Public | 
                               BindingFlags.Instance; 
    FieldInfo previewService = 
      viewer.LocalReport.GetType().GetField("m_previewService", Flags);
    MethodInfo ListRenderingExtensions = 
      previewService.FieldType.GetMethod("ListRenderingExtensions", Flags);
    IList extensions = ListRenderingExtensions.Invoke(
             previewService.GetValue(viewer.LocalReport), null) as IList; 
    Type localRenderingExtensionInfoType = Type.GetType(
       "Microsoft.Reporting.LocalRenderingExtensionInfo, 
        Microsoft.ReportViewer.Common, Version=8.0.0.0, 
        Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
    ConstructorInfo ctor = localRenderingExtensionInfoType.GetConstructor(
        Flags, null, new Type[] { typeof(string), typeof(string), 
        typeof(bool), typeof(Type), typeof(bool) }, null);
    object instance = 
      ctor.Invoke(new object[] { name, name, true, extensionType, true });
    extensions.Add(instance);
}

Now, you can call this method whenever you need to add a custom export format to the list of Report Viewer formats (neat places to consider could be the Form_Load or Page_Load event handlers in a WinForms or ASP.NET application, respectively). The viewer parameter is a Report Viewer instance, the name parameter is the name of the export format as it should appear on the list, and the extensionType parameter is the .NET type of the rendering extension:

AddExtension(ReportViewer1, "DOC - Word Document via Aspose.Words", 
             typeof(Aspose.Words.ReportingServices.DocRenderer));

I didn't find any disadvantages to this technique compared to the steps described above... it does work! So, I strongly recommend it unless you are a fan of IL and hacking :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Anton Ponomarev
Founder
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionUsing VS 2008 can't merge microsoft.reportviewer.design.dllmembergutxo29 Mar '12 - 4:21 
Hi, great article, but I can't merge microsoft.reportviewer.design.dll when I try I get the next message:
Unresolved assembly reference not allowed: Microsoft.ReportDesigner.
en System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
en System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
en System.Compiler.Ir2md.GetTypeDefOrRefOrSpecEncoded(TypeNode type)
en System.Compiler.Ir2md.VisitClass(Class Class)
en System.Compiler.Ir2md.VisitModule(Module module)
en System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
en System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation,
BinaryWriter writer)
en System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols,
Module module, Boolean delaySign, String keyFileName, String keyName)
en System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Modu
le module)
en ILMerging.ILMerge.Merge()
en ILMerging.ILMerge.Main(String[] args)
 
Is it possible that someone send me the assemblies already merged.
 
thanks
GeneralGreat article, but can't get Aspose.Words to assemblemembermbacheld6 Dec '10 - 7:57 
Thank you! This article has been VERY helpful. It would appear that Aspose has made a few changes to their .dll since this was written. I was able to get the Microsoft.ReportViewer.WinForms.Modified.il to assemble, but the Aspose will not. It looks as though much of the code is still obfuscated. This goes beyond the .mresource pieces mentioned here. Would anyone have a completed version of Aspose.Words.ReportingServices.il that I could use?
 
mbacheld@comcast.net
 
Thank you!
GeneralCant merge modified filememberkillerfrozen17 May '10 - 22:11 
Could you send me Microsoft.ReportViewer.Winforms.Modified.dll, I cant merge it.
Thank
lvkhanh87@gmail.com
QuestionCan I Use the Code?memberedmilson_hora26 Feb '10 - 3:01 
Hi, Anton,
 
I need to generate the Report in .Doc Format, there alot of steps to get it to work, there are any way to get your source code e put it in my solution and work with it at once or I have to follow all the steps to get it working?
 
thanks,
 
Edmilson
GeneralFailedmembersaialladi15 Nov '09 - 17:26 
HI,
i am failed to create Microsoft.ReportViewer.WebForms.Modified.dll,
Step 9, i am struck
Please Send me the code or DLL, if any one get succeed
its urgent
my mail id : saikrishna.alladi@gmail.com
 
Thanks & Regards
Saikrishna A
GeneralRe: Failedmemberbetori20 Apr '10 - 12:30 
i have failed in step 9 for webform dunno why, anyone who send me the dll? thx
GeneralRe: Failedmemberbetori20 Apr '10 - 12:36 
avalor5@hotmail.com
GeneralGratz!memberRodrigo Garcia31 May '09 - 21:54 
Pretty nice article!
I'm sure we can do it also by using the Microsoft.ReportingServices.WordRendering.dll assembly that is included on Microsoft Builder 2.0 instead of Aspose's.
I checked at reflector and the class WordDocumentRenderer also implements IRenderingExtension and IExtension interfaces!
Only problem is that StrongNameIdentityPermission is chained in couple referenced assemblies.
thanks again for the article!
Rodrigo
GeneralUsing Visual Studio 2008 - ReportViewer 9.0memberBart De Raeymaeker22 Apr '09 - 23:36 
Can you please send me your "Microsoft.ReportViewer.WinForms.Modified.dll" file ?
Because I am using Visual Studio 2008, it isn't possible to merge the assemblies using the microsoft.reportviewer.design.dll.
I always get the following error : "An exception occurred during merging: Unresolved assembly reference not allowed: Microsoft.ReportDesigner. at ..."
GeneralRe: Using Visual Studio 2008 - ReportViewer 9.0memberwizking14 Aug '10 - 11:37 
hey same problem here..any clues anybody?? myemail id is theviking32000@yahoo.com
GeneralReporting Service version 9.0membermabilgin20 Mar '09 - 1:49 
I failed to do by following your article.
can you provide me your "Microsoft.ReportViewer.WinForms.Modified.dll" or Microsoft.ReportViewer.WebForms.Modified.dll
My email : mabilgin@gmail.com
GeneralRe: Reporting Service version 9.0memberbetori20 Apr '10 - 12:29 
can i have that .dll!
avalor5@hotmail.com
GeneralReportin 9.0membermabilgin20 Mar '09 - 1:45 
Is there a way to make modiified version for webform? Because i couldnt achieve to compile it using the method mentioned. After step 8 i am stuck.
if anyone who have a modified version of webform, can sent to mail mabilgin@gmail.com
Generalto Anton Ponomarevmemberazizo2316 Mar '09 - 1:50 
hi try with your instructions but when i want to assemble Aspose.Words.ReportingServices.dll
it fails cause the duplicate methods and decalarations?
can you send me your modified aspose dll for sql 2005 my email azizp23@hotmail.com
thanks
Generalto bravo ninermemberazizo232 Mar '09 - 23:39 
hi try with your method but when i want to assemble Aspose.Words.ReportingServices.dll
it fails cause the duplicate methods and decalarations?
can you send me your modified aspose dll for sql 2005 my email azizp23@hotmail.com
thanks
Questionhow can i export my report without reportviewer?memberazizo2327 Feb '09 - 0:02 
hi
i want to export my report to MS word without showing reportviewer control;
by using code like. YOUR modification is ok with the control but how if i want to export it dynamically like when i ewport a report to pdf file?
 
string reportType = "PDF";
 
string mimeType;
 
string encoding;
 
string fileNameExtension;
 
string deviceInfo =
 
"" +
 
" PDF" +
 
" 21cm" +
 
" 29.7cm" +
 
" 0cm" +
 
" 0cm" +
 
" 0cm" +
 
" 0cm" +
 
"
";
 

Warning[] warnings;
 
string[] streams;
byte[] renderedBytes;
renderedBytes = localReport.Render(
reportType,
deviceInfo,
out mimeType,
out encoding,
out fileNameExtension,
out streams,
out warnings);
 
Response.Clear();
Response.ContentType = mimeType;
Response.AddHeader("content-disposition", "attachment; filename=liste." + fileNameExtension);
Response.BinaryWrite(renderedBytes);
Response.End();
QuestionAlternative to Asposememberddixon1 Dec '08 - 7:46 
Anyone know of any alternative methods than using Aspose to export it as doc. A pure microsoft solution would be nice, however we are prepared to tear apart the doc and docx file formats and write our own export if necessary.
Any suggestions to alternative methods would be greatly appreciated.
Questionhow we can do that with reportviewer 2008memberamjadshaheed19 Nov '08 - 7:32 
Hi i tried you article and i tried using 2008 dlls instead of 2005 but when i tried to merge it it was giving me error with designer.dll but with 2005 it merged ok
 
any body has modified dll to 2008 please could send to my email address which is m_amjad16@yahoo.co.uk thanks
GeneralVery Good article! However, I failed to get it work [modified]memberBill Sun31 Aug '08 - 16:32 
would you please send me the modified DLLs too? my email is kungfusoftware@gmail.com
 
Try the private Reflection Method, I got the following errors
 
The type 'Microsoft.ReportingServices.ReportRendering.IRenderingExtension' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.ReportViewer.Common, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
 
The type 'Microsoft.ReportViewer.Common.IExtension' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.ReportViewer.Common, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
 
If I follow the Article steps with Win Version. I can compile, but I got the following error when displaying report.
 
could not load tpe'Microsoft.ReportViewer.WinForms.modified.IExtension from assembly 'tpe'Microsoft.ReportViewer.WinForms.modified Vewrsion=8.0.0.0,culture=neutral. publickeytoken=null'
 
modified on Monday, September 1, 2008 11:07 AM

GeneralRe: Very Good article! However, I failed to get it workmemberAnton Ponomarev20 Sep '08 - 14:28 
It looks like Aspose has released a new version which works with Report Viewer out of the box. No need to modify any dlls.
GeneralVery useful articlememberaggallentes17 Aug '08 - 21:36 
Hi,
 
This article is very useful and it happens that I was having this kind of requirement also, I was trying to follow the steps you have provided but unfortunately I'm not yet very good in this IL things so I cannot complete it on my own.
 
Would you mind to send me also a copy of the dll itself to my email: arjaymail-developer@yahoo.com.
 
I would appreciate any response to this request...
 
Many Thanks,
 
Arjay G.
GeneralRe: Very useful articlememberAnton Ponomarev20 Sep '08 - 14:29 
Check their website. It looks like Aspose has released a new version which works with Report Viewer out of the box. No need to modify any dlls.
GeneralSource code missingmemberTBermudez31 Jul '08 - 4:54 
The source code link is broken.
GeneralRe: Source code missingmembermp252631 Jul '08 - 5:49 
Remove the double quotes from the link and it will successfully download
GeneralGood work! [modified]memberAndriy Protskiv24 Jul '08 - 21:22 
your idea is very usefull.
Now I'm writing article about using MS Reporting Services.
Can you provide me your "Microsoft.ReportViewer.WinForms.Modified.dll" too?
Please sent it to my mail *****@gmail.com.
 
Thanks,
Andriy Protskiv

modified 22 Oct '12 - 10:50.

GeneralRe: Good work!memberAndriy Protskiv1 Aug '08 - 18:45 
I wrote the article in which I pointed to your article. Thank you for your excellent idea again!
GeneralRe: Good work!memberAnton Ponomarev4 Aug '08 - 18:41 
Hi,
 
Thank you for your article, great idea too! I will try to send you the modified dll shortly; meanwhile you can use the private reflection technique recently added to the article. I'm even going to extract it into a separate article as it is much easier to implement than the original idea.
GeneralMicrosoft.ReportViewer.WinForms.Modified.dllmemberMember 411834016 Jul '08 - 1:05 
I failed to do by following your article. Confused | :confused:
can you provide me your "Microsoft.ReportViewer.WinForms.Modified.dll" ?
My email : dgarciag13@yahoo.es
 
Thanks.
GeneralRe: Microsoft.ReportViewer.WinForms.Modified.dllmemberAnton Ponomarev4 Aug '08 - 18:43 
Hi,
 
I will try to send you the modified dll shortly; meanwhile you can use the private reflection technique recently added to the article.
GeneralRe: Microsoft.ReportViewer.WinForms.Modified.dllmemberpiri_nickm17 Mar '11 - 12:19 
I've tried several times and could not merge the dlls in a single "modified.dll".someone can help me. my e-mail: jorgerobertoalvarado@gmail.com.
thanks
GeneralAbout the web forms reporting viewer changememberSyuuKoukai8 Jul '08 - 21:49 
Hi,
 
Could you support some comments about the web form process using your article?
 
I have faced some problems when try to add the doc convention funciton.
 
In the step 8: Step 8: Modify Designer Reference
 
Could you give me the value using in the web?
 
my email: koukai.chou@gmail.com.
 
Otherwise, I have merged 4 dlls into 1 web.modified dll:
 
microsoft.reportviewer.common.dll
microsoft.reportviewer.processingobjectmodel.dll
microsoft.reportviewer.webforms.dll
microsoft.reportviewer.webdesign.dll
 
Is it right?
 
Thanks a lot for your kind help.
 
Regards
Koukai
GeneralRe: About the web forms reporting viewer changememberAnton Ponomarev11 Jul '08 - 1:40 
Hi,
 
What problems have you faced? Could you please post them here or email me via the Email link?
GeneralRe: About the web forms reporting viewer changemembernanonan11 Jul '08 - 5:42 
Hi same questions with the web forms version of this method.
 
1-I've also merged these dlls :
microsoft.reportviewer.common.dll
microsoft.reportviewer.processingobjectmodel.dll
microsoft.reportviewer.webforms.dll
microsoft.reportviewer.webdesign.dll
 
2-Step 8: Modify Designer Reference : is the value mandatory for building the website ? If yes which one is it ?
 
3-Should we see the new report viewer control in the toolbox after referencing it in the website ? (think so if the hex value is ok)
 
4-We should have something like this in the page :
<%@ Register Assembly="Microsoft.ReportViewer.WebForms.Modified Version=8.0.0.0, Culture=neutral"
      Namespace="Microsoft.Reporting.WebForms.Modified" TagPrefix="rsweb" > with no token key as it has been removed when merging
 
<rsweb:ReportViewer ID="ReportViewer1" runat="server"        
      </rsweb:ReportViewer>
 
+ relative httphandler in web.config for axd stuff as required
 
Did i miss something ?
 
Thx for help,
 
jp
GeneralRe: About the web forms reporting viewer changememberAnton Ponomarev11 Jul '08 - 11:12 
Thank you for your post, too. I'll try to answer your questions after the weekend.
GeneralRe: About the web forms reporting viewer changemembernanonan16 Jul '08 - 2:09 
Hello,
 
Some news ?
 
Thx !
 
jp.
GeneralRe: About the web forms reporting viewer changememberAnton Ponomarev23 Jul '08 - 7:01 
Hi again and sorry for delay. I tried to apply the solution to the WebForms control and yes, it turned out you have to apply some additional changes in order to make the control working. The task is to make all HTTP handler references (both in the assembly and in the web.config file) point to the same place - namely Microsoft.ReportViewer.WebForms.Modified.dll. I've modified the article, please read the step 7B for instructions.
 
Regarding your questions:
 
1. All is correct.
2. No, it is not mandatory, but it just adds some custom actions in design mode. You can omit this step, simply remove the Designer attribute and that's it.
3. You should add it to the toolbox explicitly (like any other control). What hex value do you mean?
4. All this stuff is added automatically if you drop the control from the toolbox. You'll only have to alter web.config as described in 7B.
 
Thanks. Hope it helps.
QuestionWhy don't you just use private reflection?memberBravo Niner30 Jun '08 - 12:36 
This seems like a lot of work to do something that you can easily do with reflection. Why not just create a new instance of the "LocalRenderingExtensionInfo" (marked as internal) and then add it to the underlying list?
 
Example:
 
FieldInfo m_previewService = viewer.LocalReport.GetType().GetField("m_previewService", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo ListRenderingExtensions = m_previewService.FieldType.GetMethod("ListRenderingExtensions", BindingFlags.Public | BindingFlags.Instance);
 
IList extensions = ListRenderingExtensions.Invoke(m_previewService.GetValue(viewer.LocalReport), null) as IList;
 
if (extensions.Count > 0)
{
    ConstructorInfo ctor = extensions[0].GetType().GetConstructor
    (
        BindingFlags.NonPublic | BindingFlags.Instance,
        null,
        new Type[] { typeof(string), typeof(string), typeof(bool), typeof(Type), typeof(bool) },
        null
    );
 
    object instance = ctor.Invoke(new object[]
    {
            "MyExtensionName",
            "MyExtensionName",
            true, 
            typeof(MyExtension), 
            true
    });
 
    extensions.Add(instance);
}

AnswerRe: Why don't you just use private reflection?memberAnton Ponomarev3 Jul '08 - 11:30 
Thanks, but when and how do you suggest this code is executed? Have you successfully tried it?
GeneralRe: Why don't you just use private reflection?memberBravo Niner6 Jul '08 - 6:43 
When you would use it: Whenever you want to inject a rendering extension into the LocalReport's list of available extensions.
 
How you would use it: Execute the code on an instance of ReportViewer
 
Have you successfully tried it: Yes, many times
 
Here's a working example (note, this is for the Microsoft.Reporting.WebForms.ReportViewer Type):
 
 
using System;
using System.Reflection;
using System.Collections;
using Microsoft.Reporting.WebForms;
using Microsoft.ReportingServices.ReportRendering;
 

 
static void AddExtension(ReportViewer viewer, string name, Type extensionType)
{
    const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
    FieldInfo m_previewService = viewer.LocalReport.GetType().GetField("m_previewService", Flags); 
    MethodInfo ListRenderingExtensions = m_previewService.FieldType.GetMethod("ListRenderingExtensions", Flags); 
    IList extensions = ListRenderingExtensions.Invoke(m_previewService.GetValue(viewer.LocalReport), null) as IList;
    Type localRenderingExtensionInfoType = Type.GetType("Microsoft.Reporting.LocalRenderingExtensionInfo, Microsoft.ReportViewer.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
    ConstructorInfo ctor = localRenderingExtensionInfoType.GetConstructor(Flags, null, new Type[] { typeof(string), typeof(string), typeof(bool), typeof(Type), typeof(bool) }, null);
    object instance = ctor.Invoke(new object[] { name, name, true, extensionType, true });
    extensions.Add(instance);
}
 
static void Main(string[] args)
{
    using (ReportViewer reportViewer = new ReportViewer())
    {
        reportViewer.ProcessingMode = ProcessingMode.Local;
 
        AddExtension(reportViewer, "MyRenderingExtension", typeof(MyRenderingExtension));
 
        reportViewer.LocalReport.ReportPath = @"D:\temp\test.rdlc";
 
        string mimeType, encoding, fileNameExtension;
        string[] streams;
        Warning[] warnings;
 
        reportViewer.LocalReport.Render("MyRenderingExtension", null, out mimeType, out encoding, out fileNameExtension, out streams, out warnings);
    }
}
 
So, as you can see, I have a type named MyRenderingExtension. This type implements the Microsoft.ReportingServices.ReportRendering.IRenderingExtension interface. To test this code you can simply create your own Type that implements this interface and then set breakpoints on the methods and properties.
GeneralRe: Why don't you just use private reflection?memberAnton Ponomarev6 Jul '08 - 14:55 
Thanks, looks nice. I'll try it and update the article when I've got time.
 
Why I didn't use it? Apparently my hacking skills get outdated as I get older. I guess next generation hackers will be using LINQ to do that sort of things Smile | :)
GeneralRe: Why don't you just use private reflection?memberAnton Ponomarev11 Jul '08 - 1:32 
I tried your solution and unfortunately it failed. I added a reference to the two Microsoft.ReportingServices.XXX assemblies (to implement the interface) and one of them threw a BadImageFormatException when I tried to run the project. Maybe it's just my local problem, though. I'll keep investigating.
GeneralRe: Why don't you just use private reflection?memberBravo Niner20 Jul '08 - 17:25 
Well, it's a problem with that extension then. The technique I mentioned here works and I have successfully used it to create my own custom extensions for CSV, plain text, and Word document generation.
 
To verify this approach, simply create a class that implements IRenderingExtension and then set a breakpoint on the Render method.
GeneralRe: Why don't you just use private reflection?memberAnton Ponomarev23 Jul '08 - 7:21 
It does work!!! Although you'll anyway have to use a bit of hacking if you use a commercial rendering extension (since all of them reference Microsoft.ReportingServices.XXX assemblies), this does the trick in a much easier manner than in my article...
 
Thank you for your awesome contribution. If you don't mean, I'll add this to the article (with mentioning you as the author of the technique of course).
GeneralRe: Why don't you just use private reflection?memberBravo Niner28 Jul '08 - 4:49 
Please feel free to do that.
GeneralGetting exception durring execution. - Also... [modified]memberFosset21 May '08 - 6:02 
Get same error as DevilCow: Microsoft.Reporting.WinForms.LocalProcessingException: An error occurred during local report processing. ---> System.TypeLoadException: Could not load type 'Microsoft.ReportViewer.WinForms.Modified.IExtension' from assembly 'Microsoft.ReportViewer.WinForms.Modified, Version=8.0.0.0, Culture=neutral, PublicKeyToken=null'
 
Trying many times without succes...
Also It written ASPOSE version : version 2.0.2.0 but in screenshot of Step 3: Remove Public Keys
we can read .ver 2 : 0 : 3 : 0 (The space " : " is to bypass happy face)
 
But I have also tried this version and I get same problem...
grr i'm frustrated to try.. Smile | :)
 
modified on Wednesday, May 21, 2008 3:25 PM

GeneralRe: Getting exception durring execution. - Also...memberFosset22 May '08 - 8:22 
Like DevilCow, I retry from begin and succesfully do that..
 
Thanks for this code...
Generalrequest for Microsoft.ReportViewer.WinForms.Modified.dllmembermschua4415 May '08 - 19:27 
I failed to do by following your article.
can you provide me your "Microsoft.ReportViewer.WinForms.Modified.dll" ?
My email : openupped@yahoo.com
Thanks.
GeneralRe: request for Microsoft.ReportViewer.WinForms.Modified.dllmemberAnton Ponomarev1 Feb '10 - 0:31 
Hi,
 
Sorry for such a delay. I am glad to say that Aspose.Words for Reporting Services now supports ReportViewer out of the box, so you do not need to bother implementing all the steps descibed in the article.
 
You can read more about this and other features of Aspose.Words for Reporting Services at
http://www.aspose.com/categories/ssrs-rendering-extensions/aspose.words-for-reporting-services/default.aspx[^]
 
Thanks.
GeneralGetting exception durring execution.memberDevilCow16 Apr '08 - 14:53 
Hello,
First let me say thank you for the article!
and thank you for the sample code!
 
I am having an issue however where when I try to view the form I get an error message

Microsoft.Reporting.WinForms.LocalProcessingException: An error occurred during local report processing. ---> System.TypeLoadException: Could not load type 'Microsoft.ReportViewer.WinForms.Modified.IExtension' from assembly 'Microsoft.ReportViewer.WinForms.Modified, Version=8.0.0.0, Culture=neutral, PublicKeyToken=null'.
at Microsoft.Reporting.ControlService.ListRenderingExtensions()
at Microsoft.Reporting.WinForms.LocalReport.ListRenderingExtensions()

Perhaps I just overlooked something in the tutorial, I will go back and read it again, but I wanted to drop a quick question to see if I could get your feedback on this.
 
Thanks again!
Devilcow
GeneralRe: Getting exception durring execution.memberDevilCow17 Apr '08 - 3:06 
Well I went back through and fixed the problem, I had downloaded a newer version of the Library from Aspose, Additionally I think I missed some of the mangled declarations in my original attempt.
The end result is I have it up and running just fine now.
One other side question, does anyone know about a LIB that I can use that doesn't have the eval limitations.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 31 Jul 2008
Article Copyright 2008 by Anton Ponomarev
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid