<?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>Jonathan C Dickinson &#187; Migrated</title>
	<atom:link href="http://jonathan.dickinsons.co.za/blog/category/migrated/feed/" rel="self" type="application/rss+xml" />
	<link>http://jonathan.dickinsons.co.za/blog</link>
	<description>&#34;Jonathan Chayce Dickinson&#34;.ToString()</description>
	<lastBuildDate>Sat, 26 Feb 2011 14:21:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Wrapping on Specific Characters</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/wrapping-on-specific-characters/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/wrapping-on-specific-characters/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:27:16 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Top Hacks]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=22</guid>
		<description><![CDATA[How to inform GDI+ that a certain character can be automatically wrapped.]]></description>
			<content:encoded><![CDATA[<p>Recently on a project I was working I needed to wrap on backslash characters  (file and registry paths). Unfortunately the only help StringFormat can provide  is intelligent trimming (e.g. C:\Program Files\&#8230;.\Foo) and not wrapping  options.</p>
<p>I wasn&#8217;t so keen on writing my own text drawing/wrapping algorithm, but how  to wrap at a specific character? One of my co-workers gave me the spark I needed  to figure the whole thing out: use spaces. Now, that is a hack that is visible  to the user, and is plain nasty (C:\ Foo\ Bar just doesn&#8217;t cut it) &#8211; but we live  in a unicode world; and unicode has some very interesting characters. The one  that helps in this situation is ZERO WIDTH SPACE (U + 200B).</p>
<p>How do we use it? Simply do the following:</p>
<div class="code-header">Wrap after backslashes</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; "> string wrappable = unwrappable.Replace("\\", "\\\u200B");</pre>
<p>That will allow wraps after backslashes. I am sure you could find non-wrap  counterparts for most wrap characters as well (e.g. NON BREAK SPACE). I started  writing a util class to allow you to wrap before/after any character (that  doesn&#8217;t usually wrap) or not wrap before/after a character (that  usually wraps) - but it was decidedly boring; so I never finished it.</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/wrapping-on-specific-characters/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unbox Enum as an Integral Value</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/unbox-enum-as-an-integral-value/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/unbox-enum-as-an-integral-value/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:25:45 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=20</guid>
		<description><![CDATA[How to unbox an enum without needing to know its base type.]]></description>
			<content:encoded><![CDATA[<p>I needed a way to get the integral value of an System.Enum that is boxed. The  solution wasn&#8217;t immediately obvious after my experimentation mainly because:</p>
<ul>
<li>My enum was derived from byte (and I was trying to cast it to an int).</li>
<li>My .Net jitsu wasn&#8217;t up to scratch.</li>
</ul>
<p>Lucky thanks to a friendly guy called <a href="http://stackoverflow.com/questions/746905/get-integral-value-of-boxed-enum/746928#746928" target="_blank">Guffa</a> I managed to create generic solution.</p>
<p>As it turns out you can unbox an enum value to its base type. As such the following can be unboxed to an uint.</p>
<div class="code-header">Unboxing an enum when the underlying type is known</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">
struct FoolishStruct : uint
{
   Gluttony,
   Pride,
   Jealousy
}

object sinner = FoolishStruct.Gluttony;
uint value = (uint)sinner; // This works.
</pre>
<p>This gave me the necessary knowledge to hash out the following solution:</p>
<div class="code-header">Unboxing an enum when the underlying type is not known</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">
    /// &lt;summary&gt;
    /// Gets the integral value of an enum.
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;The enum to get the integral value of.&lt;/param&gt;
    /// &lt;returns&gt;&lt;/returns&gt;
    public static T ToIntegral&lt;T&gt;(this object value)
    {
        if(object.ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        Type rootType = value.GetType();
        if (!rootType.IsEnum)
            throw new ArgumentOutOfRangeException("value", "value must be a boxed enum.");
        Type t = Enum.GetUnderlyingType(rootType);

        switch (t.Name.ToUpperInvariant())
        {
            case "SBYTE":
                return (T)Convert.ChangeType((sbyte) value, typeof(T));
            case "BYTE":
                return (T) Convert.ChangeType((byte) value, typeof(T));
            case "INT16":
                return (T) Convert.ChangeType((Int16) value, typeof(T));
            case "UINT16":
                return (T) Convert.ChangeType((UInt16) value, typeof(T));
            case "INT32":
                return (T) Convert.ChangeType((Int32) value, typeof(T));
            case "UINT32":
                return (T) Convert.ChangeType((UInt32) value, typeof(T));
            case "INT64":
                return (T) Convert.ChangeType((Int64) value, typeof(T));
            case "UINT64":
                return (T) Convert.ChangeType((UInt64) value, typeof(T));
            default:
                throw new NotSupportedException();
        }
    }
</pre>
<p>HTH.</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/unbox-enum-as-an-integral-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Use Is.Gd/Tiny Url/Bookmarklet-X in Chrome</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-use-isgdtiny-urlbookmarklet-x-in-chrome/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-use-isgdtiny-urlbookmarklet-x-in-chrome/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:22:19 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Tricks]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=14</guid>
		<description><![CDATA[How to get the most out of bookmarklets in Google Chrome.]]></description>
			<content:encoded><![CDATA[<p>This morning I got sick of having to constantly open my bookmark toolbar to  use Is.Gd (it just messes with the zen of Chrome) so I tried to figure out how to have it as a shortcut.</p>
<p>One option is to name the bookmark something like !gd, while this works, you  have to type it in and press (down) twice, then enter. Not my cup of tea.</p>
<p>As it turns out Chrome doesn&#8217;t really need a query for a search engine, if  you make a engine with no location for the query string pressing Enter will  invoke it immediately.</p>
<p>So here are the steps:</p>
<ol>
<li>Right click on the link.</li>
<li>Select copy link address.</li>
<li>(Wrench) -&gt; Options</li>
<li>Basics tab.</li>
<li>To the right of &#8220;Default Search:&#8221; click Manage.</li>
<li>Click Add.</li>
<li>Give it a name (mine was Is.Gd)</li>
<li>Give it a keyword (like !isgd).</li>
<li>Paste in the URL.</li>
<li>Ok. Close. Close.</li>
<li>Right click the bookmark on your bookmark tab.</li>
<li>Delete.</li>
</ol>
<p>I tend to create bang-like keywords; just put an exclamation point in front of them.</p>
<p>The only shortcoming right now is that the bookmarks cant contain braces (&#8220;{&#8221; and &#8220;}&#8221;) and you may need to tweak them &#8211; a little. Jon Galloway has an ace bookmarklet that will <a href="http://weblogs.asp.net/jgalloway/archive/2008/08/30/msdn-low-bandwidth-bookmarklet.aspx">switch MSDN to the low bandwidth view</a>. The original format of the link is:</p>
<div class="code-header">Original bookmarklet</div>
<pre class="brush: js;">
javascript:
 if(document.cookie.indexOf('LoBandEnabled=yes')&lt;0)
 {
  document.cookie='LoBandEnabled=yes;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';
 }
 else
 {
  document.cookie='LoBandEnabled=no;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';
 }
 window.location.reload();
</pre>
<p>The first thing you can do is to strip off the <em>window.location.reload();</em>, Chrome does that in any case. The second thing you will have to do is to split it into two seperate bookmarklets.</p>
<div class="code-header">Seperate bookmarklets</div>
<pre class="brush: js; gutter: false; highlight: [1,3];">
document.cookie='LoBandEnabled=yes;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';

document.cookie='LoBandEnabled=no;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';
</pre>
<p>Finally turn them into methods and add the javascript prefix:</p>
<div class="code-header">Final bookmarklets</div>
<pre class="brush: js; gutter: false; highlight: [1,3];">
javascript:void(document.cookie='LoBandEnabled=yes;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';)

javascript:void(document.cookie='LoBandEnabled=no;path=/;domain=.microsoft.com;%20expires=Wed,%2001-Aug-2040%2008:00:00%20GMT';)
</pre>
<p>You can then create two bookmarklets. Mine were <em>!mlon</em> and <em>!mloff</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-use-isgdtiny-urlbookmarklet-x-in-chrome/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to close a XML Element using XmlTextWriter</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-close-a-xml-element-using-xmltextwriter/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-close-a-xml-element-using-xmltextwriter/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:20:34 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Top Hacks]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=12</guid>
		<description><![CDATA[You can't close an XML element using the XmlTextWriter class unless you write a conflicting state to the document (e.g. Comment). Here is how to get around that.]]></description>
			<content:encoded><![CDATA[<p>Recently with my forays into the XMPP land, I have needed to handle the case  of writing out the following XML (as is, without the closing tag):</p>
<div class="code-header">Single opening tag</div>
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">&lt;stream:stream xmlns="http://etherx.jabber.org/streams"  xmlns="jabber:client" from="bob" to="server"&gt;</pre>
<p>Note that the tag is closed. The .Net XML writer keeps that open until you  try and go into a conflicting state such as a new start tag, comment, PI, or  such. Calling Flush() simply doesn&#8217;t work.</p>
<p>My previous methodology used a comment to close it. Thus something like this  was sent out:</p>
<div class="code-header">Using a comment to transfer state</div>
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">&lt;stream:stream xmlns="http://etherx.jabber.org/streams"  xmlns="jabber:client" from="bob" to="server"&gt;&lt;!-- Start Stream --&gt;</pre>
<p>Not the best. Some clients dont like comments and the XMPP specification says  that comments SHOULD NOT be used (note, not MUST NOT), so some parsers fall  over.</p>
<p>I finally decided to give fixing it a go. I created a root XML writer class  that had one abstract method: CompleteElement(). I won&#8217;t paste that class in  because it is trivial. I cracked open Reflector and figured out if there was a  common method in XmlTextWriter that handles this. I was in luck, there was  (AutoComplete)! My first attempt simply reflected over the XmlTextWriter and  found the method, enum and enum field. It didn&#8217;t work. XmlWriter.Create() hands  out XmlWellFormedWriters (you can&#8217;t instantiate these directly, the class is  internal). So I looked at it using Reflector and I was in luck again! The only  thing that is different is the name of the method (AdvanceState) and everything  else was exactly the same.</p>
<p>Here it is (most of it is purely wrappers):</p>
<div class="code-header">Complete Streaming XML Writer</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">    /// &lt;summary&gt;
    /// Represents a streaming xml text writer.
    /// &lt;/summary&gt;
    public class StreamingXmlTextWriter : StreamingXmlWriter
    {
        private static object __autoCompleteComment;
        private static MethodInfo __autoCompleteMethod;

        static StreamingXmlTextWriter()
        {
            // Get the type.
            Type wellFormedWriter = typeof(XmlTextWriter).Assembly.GetType("System.Xml.XmlWellFormedWriter");

            // Find the method.
            __autoCompleteMethod = wellFormedWriter.GetMethod("AdvanceState", BindingFlags.Instance | BindingFlags.NonPublic);

            // Find the argument.
            Type tokenEnum = wellFormedWriter.GetNestedType("Token", BindingFlags.NonPublic);
            FieldInfo tokenField = tokenEnum.GetField("Comment", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            __autoCompleteComment = tokenField.GetValue(null);
        }

        private XmlWriter writer;
        public StreamingXmlTextWriter(Stream stream, Encoding encoding)
            : base(stream, encoding)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = encoding;
            settings.OmitXmlDeclaration = true;

            writer = XmlWriter.Create(stream, settings);
        }

        #region Wrapped Methods
        public override void Close()
        {
            writer.Close();
        }

        public override void Flush()
        {
            writer.Flush();
        }

        public override string LookupPrefix(string ns)
        {
            return writer.LookupPrefix(ns);
        }

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            if (skippedAttribute)
                return;

            writer.WriteBase64(buffer, index, count);
        }

        public override void WriteCData(string text)
        {
            if (skippedAttribute)
                return;

            writer.WriteCData(text);
        }

        public override void WriteCharEntity(char ch)
        {
            if (skippedAttribute)
                return;

            writer.WriteCharEntity(ch);
        }

        public override void WriteChars(char[] buffer, int index, int count)
        {
            if (skippedAttribute)
                return;

            writer.WriteChars(buffer, index, count);
        }

        public override void WriteComment(string text)
        {
            writer.WriteComment(text);
        }

        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            writer.WriteDocType(name, pubid, sysid, subset);
        }

        public override void WriteEndAttribute()
        {
            if (skippedAttribute)
            {
                skippedAttribute = false;
                return;
            }

            writer.WriteEndAttribute();
        }

        public override void WriteEndDocument()
        {
            writer.WriteEndDocument();
        }

        public override void WriteEndElement()
        {
            writer.WriteEndElement();
        }

        public override void WriteEntityRef(string name)
        {
            if (skippedAttribute)
                return;

            writer.WriteEntityRef(name);
        }

        public override void WriteFullEndElement()
        {
            writer.WriteFullEndElement();
        }

        public override void WriteProcessingInstruction(string name, string text)
        {
            writer.WriteProcessingInstruction(name, text);
        }

        public override void WriteRaw(string data)
        {
            writer.WriteRaw(data);
        }

        public override void WriteRaw(char[] buffer, int index, int count)
        {
            writer.WriteRaw(buffer, index, count);
        }

        private bool skippedAttribute;
        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            // XSI/XSD must not be emitted.
            if (prefix == "xmlns" || localName == "xmlns")
            {
                if (localName == "xsi" || localName == "xsd")
                {
                    skippedAttribute = true;
                    return;
                }

                ApplyNamespace(prefix, localName, ref ns);
            }
            writer.WriteStartAttribute(prefix, localName, ns);
        }

        public override void WriteStartDocument(bool standalone)
        {
            writer.WriteStartDocument(standalone);
        }

        public override void WriteStartDocument()
        {
            writer.WriteStartDocument();
        }

        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            writer.WriteStartElement(prefix, localName, ns);
        }

        public override System.Xml.WriteState WriteState
        {
            get { return writer.WriteState; }
        }

        public override void WriteString(string text)
        {
            if (skippedAttribute)
                return;

            writer.WriteString(text);
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            if (skippedAttribute)
                return;

            writer.WriteSurrogateCharEntity(lowChar, highChar);
        }

        public override void WriteWhitespace(string ws)
        {
            if (skippedAttribute)
                return;

            writer.WriteWhitespace(ws);
        }

        public override XmlWriterSettings Settings
        {
            get
            {
                return writer.Settings;
            }
        }

        public override string XmlLang
        {
            get
            {
                return writer.XmlLang;
            }
        }

        public override XmlSpace XmlSpace
        {
            get
            {
                return writer.XmlSpace;
            }
        }
        #endregion

        public override void CompleteElement()
        {
            PerformAutoComplete();
        }

        private void PerformAutoComplete()
        {
            __autoCompleteMethod.Invoke(writer, new object[] { __autoCompleteComment });
        }
    }</pre>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/how-to-close-a-xml-element-using-xmltextwriter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TFS Build Server 2005 with VS 2008 Solutions</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/tfs-build-server-2005-with-vs-2008-solutions/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/tfs-build-server-2005-with-vs-2008-solutions/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:18:08 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Top Hacks]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=10</guid>
		<description><![CDATA[How to use build VS 2008 projects on TFS Build Server 2005.]]></description>
			<content:encoded><![CDATA[<p>I am currently RNDing Continuous Integration for our build process, as well  as automating certain build processes that we use throughout the company.</p>
<p>One issue that I found is that TFS Build Server 2005 plain refuses to build  Visual Studio 2008 solution files, and after some research I found that it is  because of the SLN version number. One method involves using a shim in place of  MSBuild to call the .Net 3.5 MSBuild executable. The current shims are really  weak and don&#8217;t do the job properly, so I have made one that does.</p>
<p>It handles incoming input, as well as the standard out and error streams.  This will allow MSBuild to report back to the server on things such as compile  errors.</p>
<p>To use the tool, rename shim.exe to the name of the original file (in this  case MSBuild.exe) and replace that file. Open regedit and create (or open) a key  at HKLM\Software\jonathand\Shim. In this key place a new string value with the  original file as the name and the new file as the value. The shim will now call  the file as it should. This should theoretically also help with MSTest.exe (I  think that&#8217;s the name) when it comes to integrated unit tests.</p>
<div class="code-header">Shim tool</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading;

namespace Shim
{
    class Program
    {
        private static Process _process;

        static int Main(string[] args)
        {
            if (TargetFile == "")
            {
                Console.Error.WriteLine("No target for shim found in registry for '{0}'.", EntryAssemblyFileName);
                return 9009; // File not found.
            }

            // Create process.
            _process = new Process();
            _process.StartInfo.Arguments = Arguments;
            _process.StartInfo.CreateNoWindow = true;
            _process.StartInfo.FileName = TargetFile;
            _process.StartInfo.RedirectStandardError = true;
            _process.StartInfo.RedirectStandardInput = true;
            _process.StartInfo.RedirectStandardOutput = true;
            _process.StartInfo.UseShellExecute = false;
            _process.StartInfo.WorkingDirectory = Environment.CurrentDirectory;

            // Wire up events.
            _process.OutputDataReceived += new DataReceivedEventHandler(_process_OutputDataReceived);
            _process.ErrorDataReceived += new DataReceivedEventHandler(_process_ErrorDataReceived);
            Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);

            _process.Start();
            _process.BeginErrorReadLine();
            _process.BeginOutputReadLine();

            // Create read thread.
            Thread readThread = new Thread(ReaderWorker);
            readThread.IsBackground = true; // Terminate when I terminate.
            readThread.Start();

            _process.WaitForExit();

            return _process.ExitCode;
        }

        static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            e.Cancel = true;
            _process.Kill();
        }

        static void ReaderWorker()
        {
            while (true)
            {
                string data = Console.ReadLine();
                _process.StandardInput.WriteLine(data);
            }
        }

        static void _process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            Console.Error.WriteLine(e.Data);
        }

        static void _process_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Console.Out.WriteLine(e.Data);
        }

        private static string _targetFile;
        private static string TargetFile
        {
            get
            {
                if (_targetFile == null)
                {
                    using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\jonathand\\Shim"))
                    {
                        if (key == null)
                            _targetFile = "";
                        else
                            _targetFile = (string)key.GetValue(EntryAssembly.ToLowerInvariant(), "");
                    }
                }
                return _targetFile;
            }
        }

        private static string _arguments;
        private static string Arguments
        {
            get
            {
                if (_arguments == null)
                {
                    _arguments = Environment.CommandLine;
                    int remString = EntryAssembly.Length + 1;
                    if (_arguments.StartsWith("\""))
                        remString += 2;
                    _arguments = _arguments.Remove(0, remString);
                }
                return _arguments;
            }
        }

        private static string _entryAssembly;
        static string EntryAssembly
        {
            get
            {
                if (_entryAssembly == null)
                {
                    _entryAssembly = Assembly.GetExecutingAssembly().CodeBase;
                    if (_entryAssembly.StartsWith("file:///"))
                        _entryAssembly = _entryAssembly.Remove(0, 8);
                    _entryAssembly.Replace('/', Path.DirectorySeparatorChar);
                }
                return _entryAssembly;
            }
        }

        private static string _entryAssemblyFileName;
        private static string EntryAssemblyFileName
        {
            get
            {
                if (_entryAssemblyFileName == null)
                    _entryAssemblyFileName = Path.GetFileName(EntryAssembly).ToLowerInvariant();
                return _entryAssemblyFileName;
            }
        }
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/tfs-build-server-2005-with-vs-2008-solutions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Anonymous Configuration Elements</title>
		<link>http://jonathan.dickinsons.co.za/blog/2009/06/anonymous-configuration-elements/</link>
		<comments>http://jonathan.dickinsons.co.za/blog/2009/06/anonymous-configuration-elements/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 08:14:04 +0000</pubDate>
		<dc:creator>Jonathan Dickinson</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Migrated]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://jonathan.dickinsons.co.za/blog/?p=6</guid>
		<description><![CDATA[How to make configuration elements that support 'unknown' attributes.]]></description>
			<content:encoded><![CDATA[<h2>Introduction</h2>
<p>Recently, with my XMPP server that is RND I wanted to make an extensible  transport system. This would allow the user to build their own transport types  (such as BOSH, or hell, even Named Pipes) and wire them into the server. Each  type of transport obviously has different configuration options (such as Address  and Port for TCP and Name for Named Pipes). This posed an interesting challenge,  how do I allow the users to add whatever attributes they want to the  configuration?</p>
<div class="code-header">Configuration data</div>
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">&lt;connectTo&gt;
  &lt;add target="Transports.TcpTransport, Transports, ..." address="192.168.0.5" port="5222" /&gt;
  &lt;add target="Transports.NamedPipeTransport, ..." name="\\.\xmppServer"/&gt;
&lt;/connectTo&gt;</pre>
<p>So how can we create the functionality without hacking stuff to death?</p>
<h2>How I did it</h2>
<p>My first clue was the overridable method,  OnDeserializeUnrecognizedAttribute(). After overriding it and setting a  breakpoint I found it worked perfectly. Now the only trick was to differentiate  between declared properties and properties that we have explicitly set.</p>
<p>Luckily the base class didn&#8217;t have a wierd implementation, so calling the  base.Properties gave me the functionality I needed, so I simply stored those  properties in another collection so that I knew which ones where defined  dynamically by the user.</p>
<h2>The Code</h2>
<p>Well, here is the code to do all you have ever needed :),  well not really. In any case, it has turned out to be very useful to  me.</p>
<div class="code-header">Anonymous Configuration Elements</div>
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: true; ">
    /// &lt;summary&gt;
    /// Represents a configuration element that can support properties not
    /// defined at compile time.
    /// &lt;/summary&gt;
    public class AnonymousValueConfigurationElement : ConfigurationElement
    {
        private List&lt;string&gt; _supportedProperties = new List&lt;string&gt;();

        /// &lt;summary&gt;
        /// Gets the list of properties that are unknown.
        /// &lt;/summary&gt;
        public IEnumerable&lt;KeyValuePair&lt;string, string&gt;&gt; UnknownProperties
        {
            get
            {
                foreach (ConfigurationProperty prop in Properties)
                {
                    // Only return properties that we don't explicitly support.
                    if (!_supportedProperties.Contains(prop.Name))
                    {
                        yield return new KeyValuePair&lt;string, string&gt;(prop.Name, (string)this[prop.Name]);
                    }
                }
            }
        }

        private ConfigurationPropertyCollection _properties;
        /// &lt;summary&gt;
        /// Gets the combination of all the properties (known and unknown).
        /// &lt;/summary&gt;
        protected override ConfigurationPropertyCollection Properties
        {
            get
            {
                // Have we done this yet?
                if (_properties == null)
                {
                    // Make our property collection.
                    _properties = new ConfigurationPropertyCollection();

                    ConfigurationPropertyCollection col = base.Properties;
                    foreach (ConfigurationProperty prop in col)
                    {
                        // Add it.
                        _properties.Add(prop);
                        // Add it to our information about the supported properties.
                        _supportedProperties.Add(prop.Name);
                    }
                }
                // Return them.
                return _properties;
            }
        }

        protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
        {
            // Make a new property.
            ConfigurationProperty prop = new ConfigurationProperty(name, typeof(string));
            // Add a new property so that we can set it.
            Properties.Add(prop);
            // Set it.
            this[name] = value;
            // We handled it.
            return true;
        }
    }</pre>
<p>It is really simple, and something that maybe should have been included in  the framework, but for those who are wondering how to do it; maybe your search  is over.</p>
<p>Hope this helps!</p>
]]></content:encoded>
			<wfw:commentRss>http://jonathan.dickinsons.co.za/blog/2009/06/anonymous-configuration-elements/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

