Click here to Skip to main content
15,887,676 members
Articles / Database Development / SQL Server / SQL Server 2008

Zip Rendering Extension for SQL Server Reporting Services 2005/2008/2012

Rate me:
Please Sign up or sign in to vote.
5.00/5 (20 votes)
14 Aug 2013CPOL13 min read 204.3K   3.8K   39  
How to create and deploy a SSRS rendering extension, explained by a functional Zip Rendering extension for SSRS 2005, 2008 (R2) and 2012.
using System;
using System.Collections.Generic;
using System.Text;

using Ionic.Zip;
using System.IO;
using System.Net;
using System.Xml;

using Microsoft.ReportingServices.Interfaces;
using Microsoft.ReportingServices.ReportRendering;
using Microsoft.ReportingServices.Rendering.CsvRenderer;
using Microsoft.ReportingServices.Rendering.ExcelRenderer;
using Microsoft.ReportingServices.Rendering.ImageRenderer;

namespace ZipRenderer
{
    public class ZipRenderingProvider : IRenderingExtension
    {
        // The built-in renderers we're gonna use later.
        private PdfReport pdfRenderer;
        private CsvReport csvRenderer;
        private ExcelRenderer excelRenderer;

        // Stream to capture the result of the renderer later.
        private CreateAndRegisterStreamStream intermediateStream;
        private List<CreateAndRegisterStreamStream> intermediateStreams;

        public ZipRenderingProvider()
        {
            csvRenderer = new CsvReport();

            // Initialize the renderers.
            excelRenderer = new ExcelRenderer();
            pdfRenderer = new PdfReport();

            intermediateStreams = new List<CreateAndRegisterStreamStream>();
        }

        #region IRenderingExtension Members

        void IRenderingExtension.GetRenderingResource(CreateAndRegisterStream createAndRegisterStreamCallback, System.Collections.Specialized.NameValueCollection deviceInfo)
        {
            excelRenderer.GetRenderingResource(createAndRegisterStreamCallback, deviceInfo);
        }

        bool IRenderingExtension.Render(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            string sessionID = reportServerParameters["SessionID"];
            if (sessionID == null)
            {
                return RenderBuiltIn(report, reportServerParameters, deviceInfo, clientCapabilities, evaluateHeaderFooterExpressions, createAndRegisterStream);
            }
            else
            {
                return RenderReportViewer(report, reportServerParameters, deviceInfo, clientCapabilities, evaluateHeaderFooterExpressions, createAndRegisterStream);
            } 
        }

        bool IRenderingExtension.RenderStream(string streamName, Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            return false;
        }
        #endregion

        bool RenderReportViewer(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            string ReportFormat = deviceInfo["ZippedReportFormat"];
            if (ReportFormat == null)
            {
                throw new System.Configuration.ConfigurationErrorsException("Missing <ZippedReportFormat> configuration entry in <DeviceInfo>");
            }
            string ReportExtension = deviceInfo["ZippedReportExtension"];

            //THIS FAILS IF SessionID IS NOT SPECIFIED(AS IN: RUN FROM A SUBSCRIPTION OR THE LIKE.)
            //CHECK FIRST, IF NO SessionID THEN RUN THROUGH WEB SERVICE OR HTTP REQUEST ON REPORTSERVER ( Pass parameters, deviceinfo etc. through..)
            //TODO: RECHECK the ExcelRenderer possibility.. Re-executing an entire report sucks - should enable caching for the reports in question in that case.

            string strUri = String.Concat("http://localhost/Reports/Reserved.ReportViewerWebControl.axd",
                "?ReportSession=", reportServerParameters["SessionID"],
                "&FileName=", report.Name,
                "&ControlId=", Guid.Empty,
                "&Culture=", System.Globalization.CultureInfo.CurrentCulture.LCID.ToString(System.Globalization.CultureInfo.InvariantCulture),
                "&UICulture=", System.Globalization.CultureInfo.CurrentUICulture.LCID.ToString(System.Globalization.CultureInfo.InvariantCulture),
                "&ReportStack=1",
                "&OpType=Export",
                "&ContentDisposition=OnlyHtmlInline",
                "&Format=", ReportFormat);

            // A buffer for copying the intermediateStream to the outputStream
            // http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c
            byte[] buffer = new byte[32768];

            //Output
            //streamWriter = new StreamWriter(intermediateStream);

            //Input
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUri);

            //Credentials
            request.Credentials = System.Net.CredentialCache.DefaultCredentials;

            //Output
            HttpWebResponse response;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (Exception ex)
            {
                throw new System.Configuration.ConfigurationErrorsException("Internal error while processing the request. There may be a problem in configuration e.g. an invalid <ZippedReportFormat> entry.", ex);
            }

            //Input
            using (Stream inputStream = response.GetResponseStream())
            {
                //prepare outputstream. Contents of the zipstream will be written here.
                Stream outputStream = createAndRegisterStream(report.Name, "zip", Encoding.UTF8, "application/zip", true, StreamOper.CreateAndRegister); //this._willSeek, this._operation);
                using (MemoryStream outputMemoryStream = new MemoryStream())
                {
                    //Create a Zip output and tell it to keep the provided stream open - we use it outside the using clause
                    using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemoryStream, true))
                    {
                        zipOutput.PutNextEntry(report.Name + "." + ReportExtension);

                        // Do the actual copying.
                        // While the streams are copying, think of the
                        // possibilities: this is the place where you would
                        // be able to fire up that external PDF library
                        // and add the PDF background!
                        // But don't think too long, cause' computers are pretty fast
                        // these days and will probably have finished copying this
                        // stream by the time you even got to start reading.
                        while (true)
                        {
                            int read = inputStream.Read(buffer, 0, buffer.Length);
                            if (read <= 0)
                            {
                                break;
                            }
                            zipOutput.Write(buffer, 0, read);
                        }
                    }

                    //Write zip contents to the output
                    outputMemoryStream.WriteTo(outputStream);
                }
            }

            // Return false, because:
            // "A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format."
            // http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.reportrendering.irenderingextension.render(SQL.90).aspx
            // ... and we're obviously not doing that, are we? Are we? ARE WE?
            return false;
        }

        bool RenderBuiltIn(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            string ReportFormat = deviceInfo["ZippedReportFormat"];
            if (ReportFormat == null)
            {
                throw new System.Configuration.ConfigurationErrorsException("Missing <ZippedReportFormat> configuration entry in <DeviceInfo>");
            }

            switch(ReportFormat)
            {
                case "EXCEL":
                    return RenderExcel(report, reportServerParameters, deviceInfo, clientCapabilities, evaluateHeaderFooterExpressions, createAndRegisterStream);
                case "CSV":
                    return RenderCSV(report, reportServerParameters, deviceInfo, clientCapabilities, evaluateHeaderFooterExpressions, createAndRegisterStream);
                case "PDF":
                    return RenderPDF(report, reportServerParameters, deviceInfo, clientCapabilities, evaluateHeaderFooterExpressions, createAndRegisterStream);
                default:
                    return false; //or throw exception?
            }
        }

        // Intermediate CreateAndRegisterStream method that matches the delegate
        // Microsoft.ReportingServices.Interfaces.CreateAndRegisterStream
        // It will return a reference to a new MemoryStream, so we can get to
        // the results of the intermediate render-step later.
        public Stream IntermediateCreateAndRegisterStreamExcel(
            string name,
            string extension,
            Encoding encoding,
            string mimeType,
            bool willSeek,
            StreamOper operation)
        {
            CreateAndRegisterStreamStream crss = new CreateAndRegisterStreamExcel(name, extension, encoding, mimeType, willSeek, operation);

            if (operation == StreamOper.CreateOnly)
                intermediateStreams.Add(crss);
            else
                intermediateStream = crss;
            return crss.Stream;
        }

        // Intermediate CreateAndRegisterStream method that matches the delegate
        // Microsoft.ReportingServices.Interfaces.CreateAndRegisterStream
        // It will return a reference to a new MemoryStream, so we can get to
        // the results of the intermediate render-step later.
        public Stream IntermediateCreateAndRegisterStream(
            string name,
            string extension,
            Encoding encoding,
            string mimeType,
            bool willSeek,
            StreamOper operation)
        {
            // Create and return a new MemoryStream,
            // which will contain the results of the PDF renderer.
            CreateAndRegisterStreamStream crss = new CreateAndRegisterStreamOther(name, extension, encoding, mimeType, willSeek, operation);

            intermediateStream = crss;

            return crss.Stream;
        }

        bool RenderExcel(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            excelRenderer.SetConfiguration(this.configuration);

            // Let the built-in renderer do the hard work!
            // After this call, the rendered file will be in the intermediateStream.
            // We're just passing-through the Render parameters.
            excelRenderer.Render(
                report,
                reportServerParameters,
                deviceInfo,
                clientCapabilities,
                evaluateHeaderFooterExpressions,
                // This is the tricky part: get a delegate method to send a stream to the
                // PDF renderer, while keeping a reference to the same stream.
                // (See the IntermediateCreateAndRegisterStream method above).
                new CreateAndRegisterStream(IntermediateCreateAndRegisterStreamExcel)
            );

            // This is the actual Stream which Reporting Services uses
            // to send the result to the end-user.
            //Stream outputStream =
            //    createAndRegisterStream(_name, _extension, _encoding, _mimeType, _willSeek, _operation);

            // It took us some time to figure out why the intermediateStream,
            // while having a length, always returned an empty result upon
            // reading from it. Well, after writing to the MemoryStream,
            // the PDF renderer doesn't reset the stream's position, so
            // we have to.
            intermediateStream.Stream.Position = 0;

            // A buffer for copying the intermediateStream to the outputStream
            // http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c
            byte[] buffer = new byte[32768];

            Stream outputStream = createAndRegisterStream(report.Name, "zip", Encoding.UTF8, "application/zip", intermediateStream.WillSeek, intermediateStream.Operation);
            using (MemoryStream outputMemoryStream = new MemoryStream())
            {
                //Create a Zip output and tell it to keep the provided stream open - we use it outside the using clause
                using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemoryStream, true))
                {
                    //Read zip deviceinfo
                    try
                    {
                        if (deviceInfo["CompressionLevel"] != null)
                            zipOutput.CompressionLevel = (Ionic.Zlib.CompressionLevel)Enum.Parse(typeof(Ionic.Zlib.CompressionLevel), deviceInfo["CompressionLevel"], true);
                        if (deviceInfo["CompressionMethod"] != null)
                            zipOutput.CompressionMethod = (Ionic.Zip.CompressionMethod)Enum.Parse(typeof(Ionic.Zip.CompressionMethod), deviceInfo["CompressionMethod"], true);
                        if (deviceInfo["Strategy"] != null)
                            zipOutput.Strategy = (Ionic.Zlib.CompressionStrategy)Enum.Parse(typeof(Ionic.Zlib.CompressionStrategy), deviceInfo["Strategy"], true);
                        if (deviceInfo["Comment"] != null)
                            zipOutput.Comment = deviceInfo["Comment"];
                        if (deviceInfo["EnableZip64"] != null)
                            zipOutput.EnableZip64 = (Ionic.Zip.Zip64Option)Enum.Parse(typeof(Ionic.Zip.Zip64Option), deviceInfo["EnableZip64"], true);
                        if (deviceInfo["IgnoreCase"] != null)
                            zipOutput.IgnoreCase = Boolean.Parse(deviceInfo["IgnoreCase"]);
                        if (deviceInfo["Encryption"] != null)
                            zipOutput.Encryption = (Ionic.Zip.EncryptionAlgorithm)Enum.Parse(typeof(Ionic.Zip.EncryptionAlgorithm), deviceInfo["Encryption"], true);
                        if (deviceInfo["Password"] != null)
                            zipOutput.Password = deviceInfo["Password"];
                    }
                    catch (Exception e)
                    {
                        throw new System.Configuration.ConfigurationErrorsException("Invalid DeviceInfo configuration value", e);
                    }

                    zipOutput.PutNextEntry(intermediateStream.Name + "." + intermediateStream.Extension);
                    // Do the actual copying.
                    // While the streams are copying, think of the
                    // possibilities: this is the place where you would
                    // be able to fire up that external PDF library
                    // and add the PDF background!
                    // But don't think too long, cause' computers are pretty fast
                    // these days and will probably have finished copying this
                    // stream by the time you even got to start reading.
                    while (true)
                    {
                        int read = this.intermediateStream.Stream.Read(buffer, 0, buffer.Length);
                        if (read <= 0)
                        {
                            break;
                        }
                        zipOutput.Write(buffer, 0, read);
                    }
                }
                //Write zip contents to the output
                outputMemoryStream.WriteTo(outputStream);
            }

            // Be nice and release some hard-needed resources.
            intermediateStream.CloseStream();
            // Close other streams, too. 
            //(What happens with these? I don't know. I figure these streams are written to by the built-in renderer, then copied to the main stream.)
            foreach (CreateAndRegisterStreamStream s in intermediateStreams)
                s.CloseStream();
            intermediateStreams.Clear();

            // Return false, because:
            // "A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format."
            // http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.reportrendering.irenderingextension.render(SQL.90).aspx
            // ... and we're obviously not doing that, are we? Are we? ARE WE?
            return false;
        }

        bool RenderCSV(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            csvRenderer.SetConfiguration(this.configuration);

            // Let the built-in PDF renderer do the hard work!
            // After this call, the rendered PDF will be in the intermediateStream.
            // We're just passing-through the Render parameters.
            csvRenderer.Render(
                report,
                reportServerParameters,
                deviceInfo,
                clientCapabilities,
                evaluateHeaderFooterExpressions,
                // This is the tricky part: get a delegate method to send a stream to the
                // PDF renderer, while keeping a reference to the same stream.
                // (See the IntermediateCreateAndRegisterStream method above).
                new CreateAndRegisterStream(IntermediateCreateAndRegisterStream)
            );

            // This is the actual Stream which Reporting Services uses
            // to send the result to the end-user.
            //Stream outputStream =
            //    createAndRegisterStream(_name, _extension, _encoding, _mimeType, _willSeek, _operation);

            // It took us some time to figure out why the intermediateStream,
            // while having a length, always returned an empty result upon
            // reading from it. Well, after writing to the MemoryStream,
            // the PDF renderer doesn't reset the stream's position, so
            // we have to.
            intermediateStream.Stream.Position = 0;

            // A buffer for copying the intermediateStream to the outputStream
            // http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c
            byte[] buffer = new byte[32768];

            Stream outputStream = createAndRegisterStream(report.Name, "zip", Encoding.UTF8, "application/zip", intermediateStream.WillSeek, intermediateStream.Operation);
            using (MemoryStream outputMemoryStream = new MemoryStream())
            {
                //Create a Zip output and tell it to keep the provided stream open - we use it outside the using clause
                using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemoryStream, true))
                {
                    //Read zip deviceinfo
                    try
                    {
                        if (deviceInfo["CompressionLevel"] != null)
                            zipOutput.CompressionLevel = (Ionic.Zlib.CompressionLevel)Enum.Parse(typeof(Ionic.Zlib.CompressionLevel), deviceInfo["CompressionLevel"], true);
                        if (deviceInfo["CompressionMethod"] != null)
                            zipOutput.CompressionMethod = (Ionic.Zip.CompressionMethod)Enum.Parse(typeof(Ionic.Zip.CompressionMethod), deviceInfo["CompressionMethod"], true);
                        if (deviceInfo["Strategy"] != null)
                            zipOutput.Strategy = (Ionic.Zlib.CompressionStrategy)Enum.Parse(typeof(Ionic.Zlib.CompressionStrategy), deviceInfo["Strategy"], true);
                        if (deviceInfo["Comment"] != null)
                            zipOutput.Comment = deviceInfo["Comment"];
                        if (deviceInfo["EnableZip64"] != null)
                            zipOutput.EnableZip64 = (Ionic.Zip.Zip64Option)Enum.Parse(typeof(Ionic.Zip.Zip64Option), deviceInfo["EnableZip64"], true);
                        if (deviceInfo["IgnoreCase"] != null)
                            zipOutput.IgnoreCase = Boolean.Parse(deviceInfo["IgnoreCase"]);
                        if (deviceInfo["Encryption"] != null)
                            zipOutput.Encryption = (Ionic.Zip.EncryptionAlgorithm)Enum.Parse(typeof(Ionic.Zip.EncryptionAlgorithm), deviceInfo["Encryption"], true);
                        if (deviceInfo["Password"] != null)
                            zipOutput.Password = deviceInfo["Password"];
                    }
                    catch (Exception e)
                    {
                        throw new System.Configuration.ConfigurationErrorsException("Invalid DeviceInfo configuration value", e);
                    }

                    zipOutput.PutNextEntry(intermediateStream.Name + "." + intermediateStream.Extension);
                    // Do the actual copying.
                    // While the streams are copying, think of the
                    // possibilities: this is the place where you would
                    // be able to fire up that external PDF library
                    // and add the PDF background!
                    // But don't think too long, cause' computers are pretty fast
                    // these days and will probably have finished copying this
                    // stream by the time you even got to start reading.
                    while (true)
                    {
                        int read = this.intermediateStream.Stream.Read(buffer, 0, buffer.Length);
                        if (read <= 0)
                        {
                            break;
                        }
                        zipOutput.Write(buffer, 0, read);
                    }
                }
                //Write zip contents to the output
                outputMemoryStream.WriteTo(outputStream);
            }

            // Be nice and release some hard-needed resources.
            intermediateStream.CloseStream();
            // Close other streams, too. 
            //(What happens with these? I don't know. I figure these streams are written to by the built-in renderer, then copied to the main stream.)
            foreach (CreateAndRegisterStreamStream s in intermediateStreams)
                s.CloseStream();
            intermediateStreams.Clear();

            // Return false, because:
            // "A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format."
            // http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.reportrendering.irenderingextension.render(SQL.90).aspx
            // ... and we're obviously not doing that, are we? Are we? ARE WE?
            return false;
        }
        
        bool RenderPDF(Microsoft.ReportingServices.ReportRendering.Report report, System.Collections.Specialized.NameValueCollection reportServerParameters, System.Collections.Specialized.NameValueCollection deviceInfo, System.Collections.Specialized.NameValueCollection clientCapabilities, EvaluateHeaderFooterExpressions evaluateHeaderFooterExpressions, CreateAndRegisterStream createAndRegisterStream)
        {
            pdfRenderer.SetConfiguration(this.configuration);

            // Let the built-in PDF renderer do the hard work!
            // After this call, the rendered PDF will be in the intermediateStream.
            // We're just passing-through the Render parameters.
            pdfRenderer.Render(
                report,
                reportServerParameters,
                deviceInfo,
                clientCapabilities,
                evaluateHeaderFooterExpressions,
                // This is the tricky part: get a delegate method to send a stream to the
                // PDF renderer, while keeping a reference to the same stream.
                // (See the IntermediateCreateAndRegisterStream method above).
                new CreateAndRegisterStream(IntermediateCreateAndRegisterStream)
            );

            // This is the actual Stream which Reporting Services uses
            // to send the result to the end-user.
            //Stream outputStream =
            //    createAndRegisterStream(_name, _extension, _encoding, _mimeType, _willSeek, _operation);

            // It took us some time to figure out why the intermediateStream,
            // while having a length, always returned an empty result upon
            // reading from it. Well, after writing to the MemoryStream,
            // the PDF renderer doesn't reset the stream's position, so
            // we have to.
            intermediateStream.Stream.Position = 0;

            // A buffer for copying the intermediateStream to the outputStream
            // http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c
            byte[] buffer = new byte[32768];

            Stream outputStream = createAndRegisterStream(report.Name, "zip", Encoding.UTF8, "application/zip", intermediateStream.WillSeek, intermediateStream.Operation);
            using (MemoryStream outputMemoryStream = new MemoryStream())
            {
                //Create a Zip output and tell it to keep the provided stream open - we use it outside the using clause
                using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemoryStream, true))
                {
                    //Read zip deviceinfo
                    try
                    {
                        if (deviceInfo["CompressionLevel"] != null)
                            zipOutput.CompressionLevel = (Ionic.Zlib.CompressionLevel)Enum.Parse(typeof(Ionic.Zlib.CompressionLevel), deviceInfo["CompressionLevel"], true);
                        if (deviceInfo["CompressionMethod"] != null)
                            zipOutput.CompressionMethod = (Ionic.Zip.CompressionMethod)Enum.Parse(typeof(Ionic.Zip.CompressionMethod), deviceInfo["CompressionMethod"], true);
                        if (deviceInfo["Strategy"] != null)
                            zipOutput.Strategy = (Ionic.Zlib.CompressionStrategy)Enum.Parse(typeof(Ionic.Zlib.CompressionStrategy), deviceInfo["Strategy"], true);
                        if (deviceInfo["Comment"] != null)
                            zipOutput.Comment = deviceInfo["Comment"];
                        if (deviceInfo["EnableZip64"] != null)
                            zipOutput.EnableZip64 = (Ionic.Zip.Zip64Option)Enum.Parse(typeof(Ionic.Zip.Zip64Option), deviceInfo["EnableZip64"], true);
                        if (deviceInfo["IgnoreCase"] != null)
                            zipOutput.IgnoreCase = Boolean.Parse(deviceInfo["IgnoreCase"]);
                        if (deviceInfo["Encryption"] != null)
                            zipOutput.Encryption = (Ionic.Zip.EncryptionAlgorithm)Enum.Parse(typeof(Ionic.Zip.EncryptionAlgorithm), deviceInfo["Encryption"], true);
                        if (deviceInfo["Password"] != null)
                            zipOutput.Password = deviceInfo["Password"];
                    }
                    catch (Exception e)
                    {
                        throw new System.Configuration.ConfigurationErrorsException("Invalid DeviceInfo configuration value", e);
                    }

                    zipOutput.PutNextEntry(intermediateStream.Name + "." + intermediateStream.Extension);
                    // Do the actual copying.
                    // While the streams are copying, think of the
                    // possibilities: this is the place where you would
                    // be able to fire up that external PDF library
                    // and add the PDF background!
                    // But don't think too long, cause' computers are pretty fast
                    // these days and will probably have finished copying this
                    // stream by the time you even got to start reading.
                    while (true)
                    {
                        int read = this.intermediateStream.Stream.Read(buffer, 0, buffer.Length);
                        if (read <= 0)
                        {
                            break;
                        }
                        zipOutput.Write(buffer, 0, read);
                    }
                }
                //Write zip contents to the output
                outputMemoryStream.WriteTo(outputStream);
            }

            // Be nice and release some hard-needed resources.
            intermediateStream.CloseStream();
            // Close other streams, too. 
            //(What happens with these? I don't know. I figure these streams are written to by the built-in renderer, then copied to the main stream.)
            foreach (CreateAndRegisterStreamStream s in intermediateStreams)
                s.CloseStream();
            intermediateStreams.Clear();

            // Return false, because:
            // "A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format."
            // http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.reportrendering.irenderingextension.render(SQL.90).aspx
            // ... and we're obviously not doing that, are we? Are we? ARE WE?
            return false;
        }

        #region IExtension Members

        string IExtension.LocalizedName
        {
            get { return description; }
        }

        private string description = "Zipped";
        private string configuration;

        /// <summary>
        /// Process XML data stored in the configuration file
        /// </summary>
        /// <param name="configuration">The XML string from the configuration file that contains extension configuration data.</param>
        void IExtension.SetConfiguration(string configuration)
        {
            this.configuration = configuration;

            // Create the document and load the Configuration element    
            XmlDocument doc = new XmlDocument();

            try
            {
                //  Creates the XML reader from the stream 
                //  and moves it to the correct node
                using (StringReader m_srdr = new StringReader(configuration))
                {
                    using (XmlReader m_xrdr = XmlReader.Create(m_srdr))
                    {
                        m_xrdr.MoveToContent();
                        doc.LoadXml(m_xrdr.ReadOuterXml());
                    }
                }

                if (doc.DocumentElement.Name == "DeviceInfo")
                {
                    foreach (XmlNode child in doc.DocumentElement.ChildNodes)
                    {
                        if (child.Name == "ZipExtensionDescription")
                        {
                            description = child.InnerText;
                            break;
                        }
                    }
                }

                //(Example)
                //// For each printer in the configuration data, add it to the list of printers
                //m_printers = new ArrayList();
                //if (doc.DocumentElement.Name == "Printers")
                //{
                //    foreach (XmlNode child in doc.DocumentElement.ChildNodes)
                //    {
                //        m_printers.Add(child.InnerText);
                //    }
                //}
            }

            catch (Exception ex)
            {
                throw new Exception("Failed to retrieve configuration data: " + ex.Message);
            }
        }

        #endregion
    }

    abstract internal class CreateAndRegisterStreamStream
    {
        #region Attributes and Properties

        string name;

        internal string Name
        {
            get { return name; }
            set { name = value; }
        }
        string extension;

        internal string Extension
        {
            get { return extension; }
            set { extension = value; }
        }
        Encoding encoding;

        internal Encoding Encoding
        {
            get { return encoding; }
            set { encoding = value; }
        }
        string mimeType;

        internal string MimeType
        {
            get { return mimeType; }
            set { mimeType = value; }
        }
        bool willSeek;

        internal bool WillSeek
        {
            get { return willSeek; }
            set { willSeek = value; }
        }
        StreamOper operation;

        internal StreamOper Operation
        {
            get { return operation; }
            set { operation = value; }
        }

        protected Stream stream;

        internal Stream Stream
        {
            get { return stream; }
            set { stream = value; }
        }

        abstract internal void CloseStream();

        #endregion

        internal CreateAndRegisterStreamStream(string name,
            string extension,
            Encoding encoding,
            string mimeType,
            bool willSeek,
            StreamOper operation)
        {
            this.name = name;
            this.encoding = encoding;
            this.extension = extension;
            this.mimeType = mimeType;
            this.operation = operation;
            this.willSeek = willSeek;
        }
    }

    internal class CreateAndRegisterStreamOther : CreateAndRegisterStreamStream
    {
        internal CreateAndRegisterStreamOther(string name,
            string extension,
            Encoding encoding,
            string mimeType,
            bool willSeek,
            StreamOper operation)
            : base(name, extension, encoding, mimeType, willSeek, operation)
        {
            stream = new MemoryStream();
        }

        override internal void CloseStream()
        {
            stream.Close();
        }
    }

    internal class CreateAndRegisterStreamExcel : CreateAndRegisterStreamStream
    {
        internal CreateAndRegisterStreamExcel(string name,
            string extension,
            Encoding encoding,
            string mimeType,
            bool willSeek,
            StreamOper operation)
            : base(name, extension, encoding, mimeType, willSeek, operation)
        {
            stream = new UnclosableMemoryStream();
        }

        override internal void CloseStream()
        {
            ((UnclosableMemoryStream)stream).AllowClose = true;
            stream.Close();
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer
Netherlands Netherlands
I am a database-oriented software developer .. or an application-oriented database engineer, depending on the situation.

In past 7 or so years I have created stuff ranging from Windows Services to WPF, from GDI+ to SSIS packages and SSRS extensions.

My goal is to improve life and work for others - which should be the ultimate goal of any good software project.
And my articles on Codeproject can hopefully make you do the same.

Comments and Discussions