<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Broes</title>
	<atom:link href="http://www.broes.nl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.broes.nl</link>
	<description>Mac OS X Widgets and more!</description>
	<lastBuildDate>Wed, 02 Nov 2011 13:30:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Profiling on SQL Server Express</title>
		<link>http://www.broes.nl/2011/10/profiling-on-sql-server-express/</link>
		<comments>http://www.broes.nl/2011/10/profiling-on-sql-server-express/#comments</comments>
		<pubDate>Wed, 19 Oct 2011 15:34:12 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=324</guid>
		<description><![CDATA[I&#8217;m working on a new web app using ASP.NET MVC 3 and Entity Framework 4.1. One thing lacking in EF 4.1 is the ability to monitor the actual queries sent to the SQL database, like you could do with Linq2SQL&#8217;s DataContext.Log. The machine I&#8217;m working on has SQL Server Express. The Express version doesn&#8217;t come [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m working on a new web app using ASP.NET MVC 3 and Entity Framework 4.1. One thing lacking in EF 4.1 is the ability to monitor the actual queries sent to the SQL database, like you could do with <a title="Linq to SQL DataContext.Log property" href="http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.log.aspx" target="_blank">Linq2SQL&#8217;s DataContext.Log</a>.</p>
<p>The machine I&#8217;m working on has SQL Server Express. The Express version doesn&#8217;t come with the SQL Profiler. So without Profiler and without a log function on <a title="Entity Framework 4.1. DbContext " href="http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(VS.103).aspx" target="_blank">Entity Framework&#8217;s DbContext</a>, what&#8217;s a developer to do?</p>
<p>Well, as it turns out, profiling is part of the SQL Server (any version, including Express), the SQL Profiler application is just one way to access the profiling functionality. Some people even argue it&#8217;s <a title="The Server-side Trace: What, Why, and How" href="http://sqlserverpedia.com/wiki/The%5FServer-side%5FTrace%3A%5FWhat,%5FWhy,%5Fand%5FHow" target="_blank">better to do the profiling on the server itself</a>, as opposed to through SQL Profiler.</p>
<h3>Starting the trace</h3>
<p>How do you start? The easiest way to start is (you&#8217;re not going to like this) by using SQL Profiler. That&#8217;s what I did (on another machine). It&#8217;s explained on the website linked above, here are the steps:<span id="more-324"></span></p>
<ol>
<li>Open up Profiler and create a new trace.</li>
<li>Select Save to File and select a location (it doesn’t matter where, we will be changing this). Select Enable File Rollover.</li>
<li>Choose your events and columns from the Events Selection tab.</li>
<li>Run the trace and then stop it right away.</li>
<li>From the File menu, choose Export &gt; Script Trace Definition &gt; For SQL Server 2005 (or whichever is appropriate in your environment) and save the script to file.</li>
<li>Open your file in SSMS, making sure you’re connected to the instance you want to profile.</li>
</ol>
<p>This will yield a SQL script similar to the following:</p>
<pre class="brush: sql; title: ; notranslate">
/****************************************************/
/* Created by: SQL Server 2008 Profiler             */
/* Date: 10/13/2011  01:10:39 PM         */
/****************************************************/

-- Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 5

-- Please replace the text InsertFileNameHere, with an appropriate
-- filename prefixed by a path, e.g., c:\MyFolder\MyTrace. The .trc extension
-- will be appended to the filename automatically. If you are writing from
-- remote server to local drive, please use UNC path and make sure server has
-- write access to your network share

exec @rc = sp_trace_create @TraceID output, 0, N'c:\SqlTrace\Trace', @maxfilesize, NULL
if (@rc != 0) goto error

-- Client side File and Table cannot be scripted

-- Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 11, 2, @on
exec sp_trace_setevent @TraceID, 11, 14, @on
exec sp_trace_setevent @TraceID, 11, 12, @on
exec sp_trace_setevent @TraceID, 13, 1, @on
exec sp_trace_setevent @TraceID, 13, 14, @on
exec sp_trace_setevent @TraceID, 13, 12, @on

-- Set the Filters
declare @intfilter int
declare @bigintfilter bigint

-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select TraceID=@TraceID
goto finish

error:
select ErrorCode=@rc

finish:
go
</pre>
<p>When you execute the above SQL in SQL Server Management Studio (SSMS), it will return one row of data with a column named &#8220;TraceID&#8221; (an integer). You&#8217;ll need this TraceID to stop the trace later on, so take note of it.</p>
<h3>Stopping the trace</h3>
<p>You can interact with the trace using the <a title="sp_trace_setstatus (Transact-SQL)" href="http://msdn.microsoft.com/en-us/library/ms176034.aspx" target="_blank"><code>sp_trace_setstatus</code></a> stored procedure. To stop the trace, execute the following SQL in SSMS and replace &#8220;@TraceID&#8221; with the number returned from the previous query.</p>
<pre class="brush: sql; title: ; notranslate">
exec sp_trace_setstatus @TraceID, 0
</pre>
<h3>Delete the trace definition</h3>
<p>Stopping the trace doesn&#8217;t delete it. By deleting I don&#8217;t mean to delete the trace file (like &#8220;c:\SqlTrace\Trace&#8221; in the query above), but deleting this trace&#8217;s definition from the SQL server. The following query will delete the trace from the SQL server (again, replace &#8220;@TraceID&#8221; with the trace ID you got from executing the first query mentioned above):</p>
<pre class="brush: sql; title: ; notranslate">
exec sp_trace_setstatus @TraceID, 2
</pre>
<h3>Read from the trace</h3>
<p>If the above went well, you will now have a trace file (like c:\SqlTrace\Trace.trc). If you open it in NotePad, you&#8217;ll notice it&#8217;s not in a plain text or other readable format. So how can you read from the trace without needing SQL Server Profiler? Pretty simple, using <a href="http://msdn.microsoft.com/en-us/library/ms188425.aspx" title="fn_trace_gettable (Transact-SQL)" target="_blank"><code>fn_trace_gettable</code></a>. Execute the following query in SSMS to read from the trace file:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT *
FROM ::fn_trace_gettable('c:\SqlTrace\Trace.trc', default)
</pre>
<h3>Oops, forgot the Trace ID?</h3>
<p>Don&#8217;t worry, run the <a href="http://msdn.microsoft.com/en-us/library/ms173875.aspx" title="fn_trace_getinfo (Transact-SQL)" target="_blank"><code>fn_trace_getinfo</code></a> function. The first column lists all existing TraceIDs.</p>
<pre class="brush: sql; title: ; notranslate">
SELECT * FROM ::fn_trace_getinfo(0)
</pre>
<p>And that&#8217;s it! You can now profile any SQL Server version, be it Express, Standard or Enterprise, without using SQL Profiler.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2011/10/profiling-on-sql-server-express/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fighting “General SQL Error” in Dynamics CRM custom workflows</title>
		<link>http://www.broes.nl/2011/09/fighting-general-sql-error-in-dynamics-crm-custom-workflows/</link>
		<comments>http://www.broes.nl/2011/09/fighting-general-sql-error-in-dynamics-crm-custom-workflows/#comments</comments>
		<pubDate>Thu, 22 Sep 2011 14:33:38 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Dynamics CRM]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=300</guid>
		<description><![CDATA[Working for my client Valx, I had some serious problems in custom workflows for Microsoft Dynamics CRM 4. Every now and then, a workflow would throw a SoapException. I always put the original exception in the InvalidPluginExecutionException (second parameter &#8220;inner exception&#8221;). Now the actual exception information you want to see is in SoapException.Detail, which is [...]]]></description>
			<content:encoded><![CDATA[<p>Working for my client <a title="Valx trailer axles" href="http://www.valx.eu" target="_blank">Valx</a>, I had some serious problems in custom workflows for Microsoft Dynamics CRM 4. Every now and then, a workflow would throw a SoapException. I always put the original exception in the <a title="InvalidPluginExecutionException Constructor" href="http://msdn.microsoft.com/en-us/library/bb959579.aspx#v4d0_sp5500r_invalidpluginexecutionexceptionconstructorstr2" target="_blank">InvalidPluginExecutionException</a> (second parameter &#8220;inner exception&#8221;). Now the actual exception information you want to see is in SoapException.Detail, which is an XmlNode. You won&#8217;t see that info if you just put the SoapException in the InvalidPluginExecutionException&#8217;s inner exception. Therefore, I use the following try/catch construction in each custom workflow&#8217;s <a href="http://msdn.microsoft.com/en-us/library/system.workflow.activities.sequenceactivity.execute(VS.90).aspx" title="SequenceActivity.Execute method" target="_blank">Execute method</a> to see some relevant information in the WorkflowLog view of the Dynamics CRM database.</p>
<p><span id="more-300"></span></p>
<pre class="brush: csharp; title: ; notranslate">
try
{
    // do stuff
}
catch (Exception ex)
{
    string soapExceptionDetail = string.empty;
    if (ex is SoapException)
    {
        soapExceptionDetail =
            string.Format(
                &quot;\nSoapException Detail: {0}\n&quot;,
                ((SoapException)ex).Detail.InnerText
            );
    }
    throw new InvalidPluginExecutionException(
        string.Format(
            &quot;An error occurred in the {0} plug-in.{1}&quot;,
            this.GetType().ToString(), soapExceptionDetail
        ),
        ex
    );
}
</pre>
<p>Now sometimes, property names promise a bit too much. Because the error I was getting in the SoapException.Detail was the anything but helpful &#8220;Generic SQL Error&#8221; (0&#215;80044150 or 2147762512). That could be anything. By the way, the following is the SQL query I&#8217;m running on the WorkflowLog view of the organization&#8217;s Dynamics CRM database. It will only show the ones with an ErrorCode, sorted by newest date first.</p>
<pre class="brush: sql; title: ; notranslate">
SELECT TOP 1000
  [AsyncOperationIdName]
  ,[Description]
  ,[Message]
  ,[CreatedOn]
  ,[ModifiedOn]
  ,[CompletedOn]
  ,[StepName]
  ,[Status]
  ,[ErrorCode]
  ,[ActivityName]
FROM [WorkflowLog]
WHERE ErrorCode IS NOT NULL
ORDER BY ModifiedOn DESC, StepName DESC
</pre>
<p>We&#8217;re looking at the &#8220;Message&#8221; column of data here. If a plugin throws an InvalidPluginExecutionException, you&#8217;ll find the error message (provided you&#8217;ve entered one) and the stack trace (also of any inner exceptions) in this database field. Neat! But&#8230; &#8220;Generic SQL Error&#8221; won&#8217;t get us any closer to a solution.</p>
<p>Next, you&#8217;ll want to <a href="http://support.microsoft.com/kb/907490" title="How to enable tracing in Microsoft Dynamics CRM" target="_blank">switch on tracing/logging for Dynamics CRM</a>. For MS Dynamics CRM 4, this will put the trace in the following folder: <code><i>Drive</i>:\Program Files\Microsoft Dynamics CRM\Trace</code>. I tend to restart IIS and the CrmAsyncService after such a change.</p>
<p>There will be trace logs created with names like <code>servername-crmasyncservice-bin-20110922-1.log</code> or <code>servername-w3wp-wwwroot-20110922-1.log</code>. Since my custom workflows are running asynchronously, I&#8217;m first interested in the CrmAsyncService log. Pretty quickly, I could find stuff like this:</p>
<pre>
[2011-09-20 05:28:12.1] Process:CrmAsyncService |Organization:7a8005ba-c6b4-4fc7-8ba0-82a57dd75a6e |Thread:   46 |Category: Platform.Async |User: 00000000-0000-0000-0000-000000000000 |Level: Error | AsyncOperationCommand.Execute
>Exception while trying to execute AsyncOperationId: {B7627B65-38E3-E011-A4C1-000C29D1C68A} AsyncOperationType: 1 - System.Data.SqlClient.SqlException: Transaction (Process ID 71) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
   at Valx.Crm.Plugins.UpdateKitProduct.UpdateKitProductPlugin.Execute(IPluginExecutionContext context)
   at Microsoft.Crm.Asynchronous.EventOperation.InternalExecute(AsyncEvent asyncEvent)
   at Microsoft.Crm.Asynchronous.AsyncOperationCommand.Execute(AsyncEvent asyncEvent)
</pre>
<p>Alright, database deadlocks! We&#8217;re pounding that poor SQL Server too hard&#8230; Poor thing. Now in my opinion there are two obvious solutions: reduce the number of parallel/concurrent threads of the CrmAsyncService, or start throttling the database queries (or the CrmService calls). Since reducing the number of parallel/concurrent threads of the Crm Asynchronous Service is not really well-documented, I chose to look into throttling.</p>
<p>First, I make the assumption that, when the workflow catches a SoapException that mentions a &#8220;Generic SQL Error&#8221; in its Detail, it is a SQL deadlock. That&#8217;s why you need to check the trace for the actual SQL error. But as far as I could tell, in my trace log files of a couple of days, the only SQL errors were of the kind &#8220;chosen as the deadlock victim&#8221;.</p>
<p>Dynamics CRM 4 already has a <a href="http://blogs.msdn.com/b/crm/archive/2009/03/25/when-do-asynchronous-jobs-fail-suspend-or-retry.aspx" title="When do asynchronous jobs fail, suspend or retry?" target="_blank">mechanism for recovering from SQL errors, both for plugins and workflows</a>. But! You cannot throw a SQLException from a SoapException. As you can see on the above-linked page, a workflow will undoubtedly fail after throwing a InvalidPluginExecutionException. After some experimenting, I devised the following methods. These are extension methods for the <a href="http://msdn.microsoft.com/en-us/library/cc156387.aspx" title="Microsoft.Crm.Sdk.ICrmService Interface" target="_blank">ICrmService</a> and what they do is simple: if the call fails with a SoapException and the SoapException.Detail contains the text &#8220;generic sql error&#8221;, it will retry, but only after waiting. Now the waiting time is extended for each iteration. I chose 1 second times the number of iterations, you can choose so at will.</p>
<p>The wait is implemented using a Thread.Sleep(). That might not always be good, but in this case it&#8217;s great, because it will prevent spawning more threads by the asynchronous CRM service, thus creating some breathing space for this waiting thread.</p>
<p>Here are the extension methods:</p>
<pre class="brush: csharp; title: ; notranslate">
using System;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;

namespace Broes.Crm.Workflows
{
    public static class CrmServiceExtensions
    {
        /// &lt;summary&gt;
        /// Method to execute a Request on the given ICrmService.
        /// When the CrmService throws a SoapException
        /// which points to a SQL Server error,
        /// it most probably is a deadlock. Subsequently,
        /// this method will sleep the current Thread
        /// for a short while and then retry. It will retry for
        /// a maximum of five times, each time with a
        /// greater interval.
        /// &lt;/summary&gt;
        /// &lt;typeparam name=&quot;T&quot;&gt;The Response type, e.g. RetrieveResponse.&lt;/typeparam&gt;
        /// &lt;param name=&quot;crmService&quot;&gt;The ICrmService to connect to the CRM.&lt;/param&gt;
        /// &lt;param name=&quot;request&quot;&gt;The Request to execute on the CRM service.&lt;/param&gt;
        /// &lt;returns&gt;The Response object.&lt;/returns&gt;
        public static T ExecuteWithRetry&lt;T&gt;(this ICrmService crmService, Request request)
            where T : Response
        {
            T response = null;
            int i = 0;

            // Maximum of five iterations.
            while (i &lt; 5)
            {
                try
                {
                    response = (T)crmService.Execute(request);

                    // If the Execute does not throw an Exception, break the loop
                    break;
                }
                catch (System.Web.Services.Protocols.SoapException e)
                {
                    // Retry if the SoapException is a &quot;Generic SQL Error&quot;,
                    // otherwise rethrow the SoapException.
                    // &quot;Generic SQL Error&quot; might indicate a deadlock.
                    if (e.Detail.InnerText.ToLower().Contains(&quot;generic sql error&quot;))
                    {
                        ++i;
                        // Wait (sleep thread) for i * 1000 milliseconds.
                        // So, first iteration waits 1 second,
                        // while fifth iteration will wait 5 seconds.
                        System.Threading.Thread.Sleep(i * 1000);
                    }
                    else throw;
                }
            }

            if (i &gt;= 5)
            {
                throw new Exception(&quot;CrmServiceExtensions.ExecuteWithRetry: too many retries&quot;);
            }

            return response;
        }

        /// &lt;summary&gt;
        /// Method to execute a Fetch XML on a given ICrmService.
        /// When the CrmService throws a SoapException
        /// which points to a SQL Server error,
        /// it most probably is a deadlock. Subsequently,
        /// this method will sleep the current Thread
        /// for a short while and then retry. It will retry
        /// for a maximum of five times, each time with a
        /// greater interval.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;crmService&quot;&gt;The ICrmService to connect to the CRM.&lt;/param&gt;
        /// &lt;param name=&quot;fetchXml&quot;&gt;The Fetch XML to execute.&lt;/param&gt;
        /// &lt;returns&gt;The result string of the Fetch method.&lt;/returns&gt;
        public static string FetchWithRetry(this ICrmService crmService, string fetchXml)
        {
            string fetchXmlResponse = string.Empty;
            int i = 0;

            while (i &lt; 5)
            {
                try
                {
                    fetchXmlResponse = crmService.Fetch(fetchXml);

                    // If the Fetch does not throw an exception, break the while-loop.
                    break;
                }
                catch (System.Web.Services.Protocols.SoapException e)
                {
                    // Retry if the SoapException is a &quot;Generic SQL Error&quot;,
                    // otherwise rethrow the SoapException.
                    // &quot;Generic SQL Error&quot; might indicate a deadlock.
                    if (e.Detail.InnerText.ToLower().Contains(&quot;generic sql error&quot;))
                    {
                        ++i;
                        // Sleep the current thread for i * 1000 milliseconds
                        System.Threading.Thread.Sleep(i * 1000);
                    }
                    else throw;
                }
            }

            if (i &gt;= 5)
            {
                throw new Exception(&quot;CrmServiceExtensions.ExecuteWithRetry: too many retries&quot;);
            }

            return fetchXmlResponse;
        }
    }
}
</pre>
<p>Hope this helps sharing with you, because I found a ton of users battling the &#8220;Generic SQL Error&#8221; and &#8220;deadlock victim&#8221; errors in (asynchronous) plugins and workflows, but so far have not seen anyone creating a possible solution.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2011/09/fighting-general-sql-error-in-dynamics-crm-custom-workflows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PDF watermark/background Rendering Extension for SSRS – Part 2</title>
		<link>http://www.broes.nl/2011/02/pdf-watermark-background-rendering-extension-for-ssrs-part-2/</link>
		<comments>http://www.broes.nl/2011/02/pdf-watermark-background-rendering-extension-for-ssrs-part-2/#comments</comments>
		<pubDate>Fri, 11 Feb 2011 00:07:46 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[SQL Server Reporting Services]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=267</guid>
		<description><![CDATA[Updated 2011-02-22: the underlying Stream is closed when PdfWriter.Close() is called, so the code for the PdfHandler.AddBackgroundPdf() method is updated (line 79 and further). Well, I didn&#8217;t think this follow-up to Part 1 would come this quick, but here it is! Again I would like to emphasize that Jan Hoefnagels is the one who should [...]]]></description>
			<content:encoded><![CDATA[<p><em><strong>Updated</strong> 2011-02-22: the underlying Stream is closed when PdfWriter.Close() is called, so the code for the PdfHandler.AddBackgroundPdf() method is updated (line 79 and further).</em></p>
<p>Well, I didn&#8217;t think this follow-up to <a href="http://www.broes.nl/2011/02/pdf-watermarkbackground-rendering-extension-for-ssrs-part-1/" title="PDF watermark/background Rendering Extension for SSRS – Part 1">Part 1</a> would come this quick, but here it is! Again I would like to emphasize that <a href="http://nl.linkedin.com/pub/jan-hoefnagels/6/a52/5a7">Jan Hoefnagels</a> is the one who should take credit for this solution, but he isn&#8217;t the blog-writing type of guy. He&#8217;s not even a &#8220;Getting Things Done&#8221; guy. He&#8217;s a &#8220;Making Things Work&#8221; guy. And he&#8217;s pretty good at that.</p>
<p>Anyway, <a href="http://www.broes.nl/2011/02/pdf-watermarkbackground-rendering-extension-for-ssrs-part-1/" title="PDF watermark/background Rendering Extension for SSRS – Part 1">Part 1</a> showed you a general solution for an implementation of a Rendering Extension for SQL Server Reporting Services that was able to utilize the built-in (Microsoft provided) PDF Renderer, while enabling you to get the rendered PDF and do &#8220;something&#8221; with it, before sending it back to the <abbr title="SQL Server Reporting Services">SSRS</abbr> server, which would subsequently send it to the end-user as a downloadable PDF file. That was a long sentence by English standards, but by Dutch standards, that&#8217;s kind of like a normal length. In German however, you would just be getting started. This whole article could be one sentence in German and still leave room for more.</p>
<p><span id="more-267"></span></p>
<p>Stay focussed. Now this &#8220;something&#8221; would be: adding a so-called watermark or background to the PDF of the rendered result. And this background would be another PDF file, like the corporate letter design. This would retain e.g. embedded fonts and vector-based images, like the company logo. Yes, we&#8217;re those guys that are anal about quality, and bitmap just doesn&#8217;t cut it for us.</p>
<p>What has changed since the last post is the need to make the solution as dependency-free as possible. I don&#8217;t dare to use the words &#8220;Inversion of Control&#8221;, since I haven&#8217;t got the slightest idea what they mean, but I just did. It was more like: how can we create the Rendering Extension in such a way that we can set the PDF used as a background without rebuilding the extension? So it&#8217;s more about configurability. Maybe some <abbr title="Inversion of Control">IoC</abbr> as well. What do I know&#8230;</p>
<p>We cooked up two points of entry for configuration and I&#8217;m sure there are more to find:</p>
<ul>
<li>In the <a href="http://msdn.microsoft.com/en-us/library/ms156281.aspx">DeviceInfo of the RSReportServer.config</a> file.</li>
<li>In the <a href="http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.ondemandreportrendering.report.parameters.aspx">Report.Parameters</a></li>
</ul>
<p>OK, you might ask, so where do I put the PDF with the background design? Well, just add it to the Report Server using the Report Manager (hint: http://&lt;reportserver:port&gt;/Reports )!<br />
Because the <code>Report</code> class has a helper function <code>public bool GetResource(string resourcePath, out byte[] resource, out string mimeType)</code>, you are able to retrieve a resource on the report server, using a <code>resourcePath</code> that can either be relative to the report, or absolute to the report server. When you&#8217;re using the Report Manager, you can see the path to the current folder or file in the URL, it&#8217;s the <code>itemPath</code> parameter. It&#8217;s UrlEncoded, by the way, so those <code>%2F</code> are actually forward slashes <code>/</code>.</p>
<p>I could go on for hours, but let me just give you the updated code. It&#8217;s using <a href="http://itextsharp.com/">ITextSharp</a>, based on <a href="http://itextpdf.com/">IText</a>, which is an open-source PDF library, but not necessarily free. So much for IoC by the way. Ah well, this was merely a &#8220;proof of concept&#8221;, right?</p>
<pre class="brush: csharp; title: ; notranslate">
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.ReportingServices.OnDemandReportRendering;
using ImageRenderer = Microsoft.ReportingServices.Rendering.ImageRenderer;
using Interfaces = Microsoft.ReportingServices.Interfaces;

namespace Broes.ReportRendering.PDF
{

    // The &quot;PDF with background PDF renderer&quot; for SQL Server Reporting Services.
    public class PdfWithBackgroundRenderer : IRenderingExtension
    {
        // The built-in PDF renderer we're gonna use later.
        private ImageRenderer.PDFRenderer pdfRenderer;

        // Stream to capture the result of the PDF renderer later.
        private Stream intermediateStream;

        // Fields for maintaining state.
        private string _name;
        private string _extension;
        private Encoding _encoding;
        private string _mimeType;
        private bool _willSeek;
        private Interfaces.StreamOper _operation;

        // Default constructor
        public PdfWithBackgroundRenderer()
        {
            // Initialize the PDF renderer.
            pdfRenderer = new ImageRenderer.PDFRenderer();
        }

        // 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,
            Interfaces.StreamOper operation)
        {
            _name = name;
            _encoding = encoding;
            _extension = extension;
            _mimeType = mimeType;
            _operation = operation;
            _willSeek = willSeek;

            // Create and return a new MemoryStream,
            // which will contain the results of the PDF renderer.
            intermediateStream = new MemoryStream();
            return intermediateStream;
        }

        // This method will be called by Reporting Services
        // when it executes a request to render a report
        // using this renderer.
        public bool Render(Report report,
            NameValueCollection reportServerParameters,
            NameValueCollection deviceInfo,
            NameValueCollection clientCapabilities,
            ref Hashtable renderProperties,
            Interfaces.CreateAndRegisterStream createAndRegisterStream)
        {
            // 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,
                ref renderProperties,
                // 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 Interfaces.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.Position = 0;

            // A byte buffer for the background PDF.
            byte[] backgroundContent;
            // This will contain the mime-type of the background PDF.
            string backgroundType;

            // To be able to configure which background PDF to
            // use from the report designer, you could use a report parameter
            // like 'BackgroundPdfPath'. You can then store the PDF on the
            // Report Server itself!
            // The path to the PDF should be relative to the report,
            // e.g. &quot;../Background.pdf&quot; or absolute to the server,
            // e.g. &quot;/Sales/Quote/Background.pdf&quot;.
            ReportParameter pdfPath = null;
            try
            {
                pdfPath = report.Parameters[&quot;BackgroundPdfPath&quot;];
            }
            catch
            { }

            // Check if there actually was a path to the background PDF.
            if (
                pdfPath != null // ReportParameter is not null
                &amp;&amp; pdfPath.Instance != null // ... and its instance is not null
                &amp;&amp; !string.IsNullOrEmpty((string)pdfPath.Instance.Value) // ... and its value is not null
                // The GetResource method of a Report is able to get any resource
                // available on the Report Server a relative or absolute path.
                // The contents of the resource are in the 'backgroundContent' byte buffer,
                // while the mime-type is in the 'backgroundType' string.
                &amp;&amp; report.GetResource((string)pdfPath.Instance.Value, out backgroundContent, out backgroundType))
            {
                // Pass the contents of the resource to the AddBackgroundPdf helper
                // method.
                // Notice that we just assume it is a PDF. Wanna be more certain?
                // Check the mime-type in the 'backgroundType' string.
                intermediateStream = PdfHandler.AddBackgroundPdf(intermediateStream, new MemoryStream(backgroundContent));
            }
            // There was no path to a background PDF in the report
            // parameters, so we fall back to the deviceInfo.
            // DeviceInfo is configurable in the RSReportServer.config file, see
            // http://msdn.microsoft.com/en-us/library/ms156281.aspx
            // This way, you could set a default background PDF if it isn't
            // specified in the report parameters. Configurability FTW!
            else if (
                deviceInfo.AllKeys.Contains(&quot;BackgroundPdfResourcePath&quot;)
                &amp;&amp; !string.IsNullOrEmpty(deviceInfo[&quot;BackgroundPdfResourcePath&quot;])
                &amp;&amp; report.GetResource(deviceInfo[&quot;BackgroundPdfResourcePath&quot;], out backgroundContent, out backgroundType))
            {
                intermediateStream = PdfHandler.AddBackgroundPdf(intermediateStream, new MemoryStream(backgroundContent));
            }

            // 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];

            // Do the actual copying.
            while (true)
            {
                int read = intermediateStream.Read(buffer, 0, buffer.Length);
                if (read &lt;= 0) break;
                outputStream.Write(buffer, 0, read);
            }

            // Be nice and release some hard-needed resources.
            intermediateStream.Close();
            intermediateStream = null;

            // Return false, because:
            // &quot;A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format.&quot;
            // 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;
        }

        public bool RenderStream(
            string streamName,
            Report report,
            NameValueCollection reportServerParameters,
            NameValueCollection deviceInfo,
            NameValueCollection clientCapabilities,
            ref Hashtable renderProperties,
            Interfaces.CreateAndRegisterStream createAndRegisterStream)
        {
            // We'll implement this &quot;later&quot;. No, seriously.
            return false;
        }

        public string LocalizedName
        {
            // Just say what it is. In English. That's one localization...
            get { return &quot;PDF with background&quot;; }
        }

        public void SetConfiguration(string configuration)
        {
            // Did I tell you this is another place
            // where you could receive configuration options?
            // Check out:
            // http://msdn.microsoft.com/en-us/library/microsoft.reportingservices.interfaces.iextension.setconfiguration.aspx
        }

        public void GetRenderingResource(
            Interfaces.CreateAndRegisterStream createAndRegisterStreamCallback,
            NameValueCollection deviceInfo)
        {
            // We'll implement this &quot;later&quot; as well. Still don't believe me?
            // Then you've got some serious trust issues!
        }
    }
}
</pre>
<p>And of course the <code>PdfHandler</code> class referenced above.</p>
<pre class="brush: csharp; title: ; notranslate">
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace Broes.ReportRendering.PDF
{
    // Utility class to handle PDF's, obviously.
    // Uses the open source component iTextSharp:
    // http://itextsharp.com/
    public static class PdfHandler
    {
        // One method to rule them all. Since it's the only one, that is.
        public static Stream AddBackgroundPdf(Stream existingDocumentStream, Stream backgroundStream)
        {
            // This stream will hold the new (merged) document
            MemoryStream documentWithBackground = new MemoryStream();

            // Initialize a new PDF document
            Document newDocument = new Document();

            // Initialize a new PDF stream writer
            PdfWriter writer = PdfWriter.GetInstance(newDocument, documentWithBackground);

            // Initialize the background PDF stream reader
            PdfReader backgroundReader = new PdfReader(backgroundStream);

            // Initialize the existing PDF stream reader
            PdfReader reader = new PdfReader(existingDocumentStream);

            // Set the page size of the merged document to the
            // size of the first page of the existing document.
            newDocument.SetPageSize(reader.GetPageSize(1));

            // Opening the document allows you to start writing content to it,
            // but you will no longer be able to add header- or meta-information.
            newDocument.Open();

            // Get access to the contents of the writer
            PdfContentByte newContent = writer.DirectContent;

            // Get the y-offset of the background PDF with regard to the
            // existing PDF, considering its page size.
            // PDF always starts at the bottom left, and we want the
            // background to always start at the top left.
            float yDistance = 0;
            if (backgroundReader.GetPageSize(1).Height &gt; reader.GetPageSize(1).Height)
            {
                yDistance = reader.GetPageSize(1).Height - backgroundReader.GetPageSize(1).Height;
            }

            // Clean up some resources.
            backgroundReader.Close();

            // Only import the first page from the background PDF
            PdfImportedPage backgroundPage = writer.GetImportedPage(backgroundReader, 1);

            // Merge each page of the existing PDF with the background PDF
            for (int i = 1; i &lt;= reader.NumberOfPages; i++)
            {
                // Create the page
                newDocument.NewPage();
                // Add the contents of the first page of the background PDF to
                // the current page, using the y-offset.
                newContent.AddTemplate(backgroundPage, 0, yDistance);
                // Import the current page of the existing PDF.
                PdfImportedPage existingPage = writer.GetImportedPage(reader, i);
                // Add the contents of the existingPage to the current page.
                newContent.AddTemplate(existingPage, 0, 0);
            }

            // Clean up some resources.
            newDocument.Close();
            reader.Close();

            // Close the writer to properly finish the PDF document.
            // http://api.itextpdf.com/com/itextpdf/text/pdf/PdfWriter.html#close()
            writer.Close();

            // UPDATE 2011-02-22:
            // The following doesn't work, as the underlying Stream also
            // closes when you call PdfWriter.Close().
            // -- Reset the position of the MemoryStream,
            // so the next consumer will find it ready to go.
            // documentWithBackground.Position = 0; --

            // UPDATE 2011-02-22:
            // After closing the MemoryStream, it is still
            // possible to copy the contents of its underlying
            // buffer using ToArray() to a new Stream.
            return new MemoryStream(documentWithBackground.ToArray());
        }
    }
}
</pre>
<p>And that kind of wraps it up. When you want to roll this into your own Rendering Extension, don&#8217;t forget to <a href="http://msdn.microsoft.com/en-us/library/wd40t7ad.aspx">&#8220;Strong Name&#8221; your assembly</a>.<br />
You&#8217;ll need the assembly&#8217;s &#8220;Public Key Blob&#8221;, which you can find by running (from the Visual Studio Command Prompt) <code>sn -Tp [pathToAssembly]\[assemblyFileName]</code>. This post explains how you can <a href="http://sharepoint.mindsharpblogs.com/Todd/Lists/Posts/Post.aspx?List=9a04c68a%2D9ea0%2D4442%2Dbd24%2D964b0be61fdc&#038;ID=7" title="Get Public Key Token of Strong Named (Signed) Assembly Using VS.NET Tools Menu">roll the command into the VS Tools menu</a>.<br />
After that, you need to update two config files: <code>rssrvpolicy.config</code> and <code>rsreportserver.config</code>. Both are located under (by default) <code>C:\Program Files\Microsoft SQL Server\MSRS10.MSSQLSERVER\Reporting Services\ReportServer</code></p>
<p>The least hard is editing the <code>rsreportserver.config</code> file, and this is <a href="http://msdn.microsoft.com/en-us/library/ms154516.aspx" title="Deploying a Rendering Extension">pretty well-documented on this page</a>.</p>
<p>Editing the <code>rssrvpolicy.config</code> file however is a pain in the @$ß. Again, <a href="http://msdn.microsoft.com/en-us/library/ms152828.aspx">it is &#8220;documented&#8221; right here</a> (seriously, I feel so dumb reading stuff like that). Let&#8217;s just look at an example entry:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;CodeGroup class=&quot;UnionCodeGroup&quot; version=&quot;1&quot; PermissionSetName=&quot;FullTrust&quot; Name=&quot;SharePoint_Server_Strong_Name&quot; Description=&quot;This code group grants SharePoint Server code full trust. &quot;&gt;
    &lt;IMembershipCondition class=&quot;StrongNameMembershipCondition&quot; version=&quot;1&quot; PublicKeyBlob=&quot;0024000004800000940000000602000000240000525341310004000001000100AFD4A0E7724151D5DD52CB23A30DED7C0091CC01CFE94B2BCD85B3F4EEE3C4D8F6417BFF763763A996D6B2DFC1E7C29BCFB8299779DF8785CDE2C168CEEE480E570725F2468E782A9C2401302CF6DC17E119118ED2011937BAE9698357AD21E8B6DFB40475D16E87EB03C744A5D32899A0DBC596A6B2CFA1E509BE5FBD09FACF&quot; /&gt;
&lt;/CodeGroup&gt;
</pre>
<p>I would recommend just mimicking such an entry. Make up a name and description and copy the PublicKeyBlob you got from the <code>sn -Tp</code> command referenced above.</p>
<p>Finally, copy your DLL (and PDB?) as well as iTextPdf&#8217;s DLL to the Reporting Services&#8217; Report Server&#8217;s bin directory, which by default is located at <code>C:\Program Files\Microsoft SQL Server\MSRS10.MSSQLSERVER\Reporting Services\ReportServer\bin</code>.</p>
<p><strong>Update 2011-02-23:</strong> If you want to debug the assembly in Visual Studio 2010, you need to follow <a href="http://blogs.msdn.com/b/robertbruckner/archive/2010/06/20/debugging-custom-extensions-with-visual-studio-2010.aspx">these instructions</a>.</p>
<p>Don&#8217;t forget to restart the &#8220;SQL Server Reporting Services (INSTANCENAME)&#8221; under &#8220;Services&#8221;!</p>
<p>Keep a close eye on the Application Log in the Event Viewer. When something&#8217;s wrong, SSRS will start whining right there.</p>
<p>And that&#8217;s it! As you can see, sentences got a whole lot shorter while nearing the end of this post. I guess the sentence-length kind of reflects my state of mind, which now screams &#8220;exhaustion&#8221;! Good night (and yes, I realize that the chances someone else is reading this right before going to bed are rather slim)!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2011/02/pdf-watermark-background-rendering-extension-for-ssrs-part-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PDF watermark/background Rendering Extension for SSRS &#8211; Part 1</title>
		<link>http://www.broes.nl/2011/02/pdf-watermarkbackground-rendering-extension-for-ssrs-part-1/</link>
		<comments>http://www.broes.nl/2011/02/pdf-watermarkbackground-rendering-extension-for-ssrs-part-1/#comments</comments>
		<pubDate>Wed, 09 Feb 2011 14:41:11 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[SQL Server Reporting Services]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=207</guid>
		<description><![CDATA[While working on a Dynamics CRM implementation for Valx —whose website was made by Jan, Zlatan and me with Umbraco— with my dear friend and &#8220;colleague&#8221; Jan Hoefnagels, we stumbled upon a technological hurdle. We promised our client quotes, orders and invoices —as generated by SQL Server Reporting Services, which is neatly integrated into Dynamics [...]]]></description>
			<content:encoded><![CDATA[<p>While working on a Dynamics CRM implementation for <a href="http://www.valx.eu">Valx</a> —whose website was made by Jan, <a href="http://nl.linkedin.com/in/zlatanmenkovic">Zlatan</a> and me with <a href="http://umbraco.org">Umbraco</a>— with my dear friend and &#8220;colleague&#8221; <a href="http://nl.linkedin.com/pub/jan-hoefnagels/6/a52/5a7">Jan Hoefnagels</a>, we stumbled upon a technological hurdle.</p>
<p>We promised our client quotes, orders and invoices —as generated by SQL Server Reporting Services, which is neatly integrated into Dynamics CRM— with and without their own letter background, so they could use the same reports for printing on pre-printed paper (no background), as well as send the PDF through email to their clients (with background).<span id="more-207"></span></p>
<p>Sounds like a logical, real-world proposition, right? So the only thing we needed to do is create a report that has a background that can be enabled/disabled. <abbr title="SQL Server Reporting Services">SSRS</abbr> supports <a href="http://msdn.microsoft.com/en-us/library/ms155810.aspx">background-images</a>, but as we soon found out, the actual <abbr title="dots per inch">DPI</abbr> (or <abbr title="pixels per inch">PPI</abbr> since it&#8217;s a bitmap) of the image is neglected. Image rendering in SSRS is hard-coded at <del datetime="2011-02-10T22:29:27+00:00">72</del> 96DPI. Not only that, it will assume the original image is 96DPI, so a 300DPI image will end up being rendered appr. 3.1 times larger than expected!</p>
<p>And even more so, why would I want to include a bitmap image of something I already have laying around as a vector file (namely a PDF)? It will only add up to the file size and decrease it&#8217;s quality.<br />
So the search begins, for &#8220;something&#8221; that would allow us to add a PDF background &#8220;on the fly&#8221; to a report exported as PDF from the report viewer in Dynamics CRM. As it turned out, lot&#8217;s of people searching for it, but no conclusive answer or solution (hence this post).</p>
<p>We wanted it to be as integrated and reusable as possible for ourselves, as well as transparent to the end-user. Enter the world of <a href="http://msdn.microsoft.com/en-us/library/ms154606.aspx">Rendering Extensions</a>. It&#8217;s a typical Microsoft world where the documentation on MSDN lists all classes and -members (the &#8220;what&#8221;), but fails to explain the &#8220;how&#8221;. I&#8217;m sure it will improve over time (as has been the case with the Dynamics CRM 4.0 documentation), but when you need it NOW, well, look further. By the way, this is what MSDN says about implementing your own rendering extension:</p>
<blockquote><p>Writing a custom rendering extension is difficult. A rendering extension must typically support all possible combinations of report elements and requires that you implement hundreds of classes, interfaces, methods, and properties.</p></blockquote>
<p>Gee, thanks for the warning! But I don&#8217;t want that! I just want to add a PDF background… I still want to use Microsoft&#8217;s PDF rendering extension and then add the background.</p>
<p>Jan is not a guy to say: hey, let&#8217;s just leave it at that. He&#8217;s more the kind of guy that says: I can do that. If some Microsoft engineer can do it, I can do it. So he did the plumbing to create a rendering extension (implementing IRenderingExtension), registering it in SSRS… and then ground to a halt. He got it so far as to output the Report parameters to a plain text result. And it did show up as an export option in the Dynamics CRM report viewer!</p>
<p>So we sat together and step-by-step came to the following result (I&#8217;ll explain afterwards) that will actually… do exactly the same thing as the built-in PDF renderer! But as you will soon see, it allows you to do &#8220;something&#8221; with the result from the built-in PDF renderer, <strong>before</strong> sending it to the end-user. Which is kind-of exactly what we want to do.</p>
<pre class="brush: csharp; title: ; notranslate">
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.ReportingServices.OnDemandReportRendering;
using ImageRenderer = Microsoft.ReportingServices.Rendering.ImageRenderer;
using Interfaces = Microsoft.ReportingServices.Interfaces;

namespace Broes.ReportRendering.PDF
{

    public class PdfWithBackgroundRenderer : IRenderingExtension
    {
        // The built-in PDF renderer we're gonna use later.
        private ImageRenderer.PDFRenderer pdfRenderer;

        // Stream to capture the result of the PDF renderer later.
        private Stream intermediateStream;

        // Fields for maintaining state.
        private string _name;
        private string _extension;
        private Encoding _encoding;
        private string _mimeType;
        private bool _willSeek;
        private Interfaces.StreamOper _operation;

        // Default constructor
        public PdfWithBackgroundRenderer()
        {
            // Initialize the PDF renderer.
            pdfRenderer = new ImageRenderer.PDFRenderer();
        }

        // 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,
            Interfaces.StreamOper operation)
        {
            _name = name;
            _encoding = encoding;
            _extension = extension;
            _mimeType = mimeType;
            _operation = operation;
            _willSeek = willSeek;

            // Create and return a new MemoryStream,
            // which will contain the results of the PDF renderer.
            intermediateStream = new MemoryStream();
            return intermediateStream;
        }

        public bool Render(Report report,
            NameValueCollection reportServerParameters,
            NameValueCollection deviceInfo,
            NameValueCollection clientCapabilities,
            ref Hashtable renderProperties,
            Interfaces.CreateAndRegisterStream createAndRegisterStream)
        {

            // 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,
                ref renderProperties,
                // 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 Interfaces.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.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];

            // 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 = intermediateStream.Read(buffer, 0, buffer.Length);
                if (read &lt;= 0) break;
                outputStream.Write(buffer, 0, read);
            }

            // Be nice and release some hard-needed resources.
            intermediateStream.Close();

            // Return false, because:
            // &quot;A return value of true indicates that any properties added
            // to the report object model are saved into the intermediate format.&quot;
            // 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;
        }

        public bool RenderStream(
            string streamName,
            Report report,
            NameValueCollection reportServerParameters,
            NameValueCollection deviceInfo,
            NameValueCollection clientCapabilities,
            ref Hashtable renderProperties,
            Interfaces.CreateAndRegisterStream createAndRegisterStream)
        {
            // We'll implement this &quot;later&quot;. No, seriously.
            return false;
        }

        public string LocalizedName
        {
            // Just say what it is. In English. That's one localization...
            get { return &quot;PDF with background&quot;; }
        }

        public void SetConfiguration(string configuration)
        {
            // Had there been configuration options,
            // we would do something with them here.
            // Like, for instance, the path to the PDF file
            // you want to use as a background.
        }

        public void GetRenderingResource(
            Interfaces.CreateAndRegisterStream createAndRegisterStreamCallback,
            NameValueCollection deviceInfo)
        {
            // We'll implement this &quot;later&quot; as well. Still don't believe me?
            // Then you've got some serious trust issues!
        }

    }
}
</pre>
<p>What more is there to explain? If you would use a component like ABCpdf you could use something similar to <code><a href="http://www.websupergoo.com/helppdf7net/default.html?page=source%2F5-abcpdf6%2Fdoc%2F1-methods%2Fread.htm">Read(Stream stream)</a></code> to open the <code>intermediateStream</code> and add a background/watermark.<br />
After you&#8217;ve done that, get the component to return the edited PDF as a Stream and copy the contents of this Stream to the <code>outputStream</code>.</p>
<p>When we&#8217;re done implementing this extension for the client, I&#8217;ll post a <a href="http://www.broes.nl/2011/02/pdf-watermark-background-rendering-extension-for-ssrs-part-2/">Part 2</a>! How about that for a cliffhanger!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2011/02/pdf-watermarkbackground-rendering-extension-for-ssrs-part-1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>NL Weerradar widget versie 2.4</title>
		<link>http://www.broes.nl/2010/12/nl-weerradar-widget-versie-2-4/</link>
		<comments>http://www.broes.nl/2010/12/nl-weerradar-widget-versie-2-4/#comments</comments>
		<pubDate>Thu, 30 Dec 2010 14:45:30 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[NL Weerradar Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=199</guid>
		<description><![CDATA[Een update van de NL Weerradar widget voor Mac OS X Dashboard, met daarin: KNMI&#8217;s &#8220;Neerslagradar&#8221; Buienradar&#8217;s &#8220;Sneeuwradar&#8221;! Download versie 2.4!]]></description>
			<content:encoded><![CDATA[<p>Een update van de <a href="http://www.broes.nl/widgets/nlweerradar/">NL Weerradar widget voor Mac OS X Dashboard</a>, met daarin:</p>
<ul>
<li>KNMI&#8217;s &#8220;Neerslagradar&#8221;</li>
<li>Buienradar&#8217;s &#8220;Sneeuwradar&#8221;!</li>
</ul>
<p><a href="http://widgets.broes.nl/Weerradar/NL%20Weerradar.wdgt.zip" title="Download versie 2.4 van de NL Weerradar widget!">Download versie 2.4</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2010/12/nl-weerradar-widget-versie-2-4/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Brandstofprijzen Widget 1.2</title>
		<link>http://www.broes.nl/2010/07/brandstofprijzen-widget-1-2/</link>
		<comments>http://www.broes.nl/2010/07/brandstofprijzen-widget-1-2/#comments</comments>
		<pubDate>Sat, 17 Jul 2010 14:27:31 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[Brandstofprijzen Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=194</guid>
		<description><![CDATA[Athlon (de gegevensbron van de widget) had de URL van de data gewijzigd, daarom een update naar versie 1.2. Download de Brandstofprijzen widget en vergeet niet je commentaar achter te laten!]]></description>
			<content:encoded><![CDATA[<p>Athlon (de gegevensbron van de widget) had de URL van de data gewijzigd, daarom een update naar versie 1.2.</p>
<p>Download de <a href="http://www.broes.nl/widgets/brandstofprijzen/">Brandstofprijzen widget</a> en vergeet niet je <a href="http://www.broes.nl/widgets/brandstofprijzen/#respond">commentaar</a> achter te laten!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2010/07/brandstofprijzen-widget-1-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Brandstofprijzen widget update</title>
		<link>http://www.broes.nl/2009/11/brandstofprijzen-widget-update/</link>
		<comments>http://www.broes.nl/2009/11/brandstofprijzen-widget-update/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 09:47:39 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[Brandstofprijzen Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=184</guid>
		<description><![CDATA[Zoals gemeld door Ko, had Athlon Car lease (waarvan de brandstofprijzen widget zijn gegevens betrekt) het adres van de brandstofprijzen pagina gewijzigd. Daarom is de widget nu ge-update (naar versie 1.1), zodat deze weer werkt. Download de Brandstofprijzen widget en vergeet niet je commentaar achter te laten!]]></description>
			<content:encoded><![CDATA[<p>Zoals <a href="http://www.broes.nl/widgets/brandstofprijzen/#comment-5020">gemeld door Ko</a>, had Athlon Car lease (waarvan de brandstofprijzen widget zijn gegevens betrekt) het adres van de brandstofprijzen pagina gewijzigd.</p>
<p>Daarom is de widget nu ge-update (naar versie 1.1), zodat deze weer werkt.</p>
<p>Download de <a href="http://www.broes.nl/widgets/brandstofprijzen/">Brandstofprijzen widget</a> en vergeet niet je <a href="http://www.broes.nl/widgets/brandstofprijzen/#respond">commentaar</a> achter te laten!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2009/11/brandstofprijzen-widget-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NL Weerradar widget en Snow Leopard: hulp gevraagd!</title>
		<link>http://www.broes.nl/2009/08/nl-weerradar-widget-en-snow-leopard-hulp-gevraagd/</link>
		<comments>http://www.broes.nl/2009/08/nl-weerradar-widget-en-snow-leopard-hulp-gevraagd/#comments</comments>
		<pubDate>Tue, 25 Aug 2009 14:32:49 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[NL Weerradar Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=181</guid>
		<description><![CDATA[Gebruiker Dave meldt net dat de NL Weerradar widget niet goed werkt onder de nieuwste versie van Mac OS X, Snow Leopard. Mocht je hetzelfde ondervinden en je kent de weg in Mac OS X, dan stel ik het zeer op prijs wanneer je de zeer waarschijnlijk aanwezige foutmelding in de Console, in de vorm [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.broes.nl/2009/01/nl-weerradar-widget-versie-22/#comment-4872">Gebruiker Dave meldt net</a> dat de NL Weerradar widget niet goed werkt onder de nieuwste versie van Mac OS X, Snow Leopard.<br />
Mocht je hetzelfde ondervinden en je kent de weg in Mac OS X, dan stel ik het zeer op prijs wanneer je de zeer waarschijnlijk aanwezige foutmelding in de Console, in de vorm van:<br />
<code>24-08-09 14:46:02 [0x0-0xc00c].com.apple.dock[257] Melding..... </code><br />
hieronder in de comments zou kunnen plaatsen.</p>
<p>Zo kan er zo snel mogelijk weer gebruik worden gemaakt van de NL Weerradar widget onder Snow Leopard!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2009/08/nl-weerradar-widget-en-snow-leopard-hulp-gevraagd/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Nieuwe widget: Brandstofprijzen</title>
		<link>http://www.broes.nl/2009/06/nieuwe-widget-brandstofprijzen/</link>
		<comments>http://www.broes.nl/2009/06/nieuwe-widget-brandstofprijzen/#comments</comments>
		<pubDate>Fri, 05 Jun 2009 18:26:43 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[Brandstofprijzen Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=154</guid>
		<description><![CDATA[Het was weer tijd voor een nieuwe widget, ditmaal één gericht op de Nederlandse Mac gebruikers. Ik maak al een tijdje gebruik van de website van Athlon Car Lease, &#8220;Goedkoper Tanken&#8221;, waarvan de prijsgegevens volgens hen afkomstig zijn van de vloot leaserijders die met een tankpas tankt. Ik vind het in ieder geval de meest [...]]]></description>
			<content:encoded><![CDATA[<p>Het was weer tijd voor een nieuwe widget, ditmaal één gericht op de Nederlandse Mac gebruikers. Ik maak al een tijdje gebruik van de website van <a href="http://www.athloncarlease.com/athlon-nl/Producten-diensten/Online-diensten/Goedkoper-tanken/" target="_blank">Athlon Car Lease, &#8220;Goedkoper Tanken&#8221;</a>, waarvan de prijsgegevens volgens hen afkomstig zijn van de vloot leaserijders die met een tankpas tankt. Ik vind het in ieder geval de meest betrouwbare site m.b.t. de prijs van brandstofgegevens. Wat miste was een eenvoudige interface met link naar Google Maps.</p>
<p>Die is er nu voor Mac OS X Dashboard: de <a href="http://www.broes.nl/widgets/brandstofprijzen/">Brandstofprijzen Widget.<br />
<img src="http://www.broes.nl/wp-content/uploads/2009/06/brandstofprijzen_widget_screenshot_full.png" alt="Brandstofprijzen Widget" title="Brandstofprijzen Widget" width="358" height="248" class="centered" /></a></p>
<p>Geef de postcode, de afstand en het brandstoftype op en de widget haalt de gegevens op en presenteert de resultaten overzichtelijk. Bladeren door de resultaten gaat d.m.v. de knopjes onderaan. Klikken op het adres opent de locatie op Google Maps. De resultaten zijn voorzien van de logo&#8217;s van de bekende tankstations.</p>
<p>De ingevoerde gegevens en de laatste zoekresultaten worden bewaard, zodat deze ook na een herstart van de computer of de widget direct beschikbaar zijn.</p>
<p>Downloaden van de widget en commentaar op de <a href="http://www.broes.nl/widgets/brandstofprijzen/">pagina van de Brandstofprijzen Widget</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2009/06/nieuwe-widget-brandstofprijzen/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Euro Stocks widget version 1.5</title>
		<link>http://www.broes.nl/2009/01/euro-stocks-widget-version-15/</link>
		<comments>http://www.broes.nl/2009/01/euro-stocks-widget-version-15/#comments</comments>
		<pubDate>Fri, 30 Jan 2009 13:06:00 +0000</pubDate>
		<dc:creator>Broes</dc:creator>
				<category><![CDATA[Euro Stocks Widget]]></category>
		<category><![CDATA[Widgets]]></category>

		<guid isPermaLink="false">http://www.broes.nl/?p=123</guid>
		<description><![CDATA[After the appearance of some gibberish text on the widget, it was inevitably time to update. This is a minor update, just to get things going again. In addition, user David B. mailed me in September already :S with a helpful addition: the ability to re-sort the stocks, using buttons on the back of the [...]]]></description>
			<content:encoded><![CDATA[<p>After the appearance of some gibberish text on the widget, it was inevitably time to update. This is a minor update, just to get things going again.<br />
In addition, user David B. mailed me in September already :S with a helpful addition: the ability to re-sort the stocks, using buttons on the back of the widget. This functionality is now included.</p>
<p><a href="http://widgets.broes.nl/EuroStocks/download.php?item=EuroStocks.wdgt.zip">Download version 1.5 of the Euro Stocks widget</a>.</p>
<p>For comments, please go to the <a href="http://www.broes.nl/widgets/eurostocks/">Euro Stocks widget&#8217;s page</a>!</p>
<p>Enjoy!</p>
<p><strong>Note to users who update.</strong> <em>Please write down the codes of your indices/stocks/funds before you update!</em> Due to a design decision made earlier (to allow multiple instances of the widget, all with their own preferences) and the fact that Apple doesn’t provide a way to keep the unique widgetID between updates, the preferences of your widget won’t be retained between updates.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.broes.nl/2009/01/euro-stocks-widget-version-15/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

