<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://msmvps.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Joacim's view on stuff : codedom</title><link>http://msmvps.com/blogs/joacim/archive/tags/codedom/default.aspx</link><description>Tags: codedom</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP2 (Build: 40407.4157)</generator><item><title>Using the CodeDom to do scripting with .Net</title><link>http://msmvps.com/blogs/joacim/archive/2009/06/16/using-the-codedom-to-do-scripting-with-net.aspx</link><pubDate>Tue, 16 Jun 2009 04:24:55 GMT</pubDate><guid isPermaLink="false">d67277c4-116b-43f1-b688-e9ef184ea916:1695554</guid><dc:creator>Joacim Andersson</dc:creator><slash:comments>7</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://msmvps.com/blogs/joacim/rsscomments.aspx?PostID=1695554</wfw:commentRss><comments>http://msmvps.com/blogs/joacim/archive/2009/06/16/using-the-codedom-to-do-scripting-with-net.aspx#comments</comments><description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The company I work for, &lt;a href="http://www.interactivemedica.com" target="_blank"&gt;Interactive Medica&lt;/a&gt;, provides &lt;a href="http://en.wikipedia.org/wiki/Software_as_a_service" target="_blank"&gt;SaaS&lt;/a&gt; business solutions for the pharmaceutical industry. Many of our clients receive data from third-parties which they need us to import into our system. This is pretty straight-forward and common in many industries. The data arrives in different formats, CSV files (or other delimited text files), Access database, &lt;a href="http://www.neopoleon.com/home/blogs/neo/archive/2003/09/29/5458.aspx" target="_blank"&gt;Excel Spreadsheets&lt;/a&gt;, and so on. Sometimes the files are zipped and sometimes not. Sometimes the files are uploaded to us via FTP and at other times we need to pull the data, also usually via FTP.&lt;/p&gt;  &lt;p&gt;Any developer have at some time or another been acquired to do something like the above, shuffling data from one source to another. Luckily, using ADO.Net, it&amp;#39;s not very hard to do so, as long as you have the correct &lt;a href="http://www.connectionstrings.com/" target="_blank"&gt;connection string&lt;/a&gt;. Anyhow, when I first started at IM they only had a few of these import programs, which all worked but the problem was that they were all hard coded, by which I mean that any changes made to the file that needed to be imported meant that we had to open up the source code for the import program and make the changes, recompile and deploy it back to the server. This was more than tedious. Some of them were also rather old, written in VB6 in a VBScript style manner, meaning no Option Explicit, new variable names created at the fly and the developer seemed to think that reusing one variable more than once wasn&amp;#39;t necessary when you could create a new one, all with nifty names like Foo, Q, P, S1, M12, and so on that didn&amp;#39;t give a clue at what they contained or was used for. Global scooping also apparently seemed like a good idea, and of course the original developer had since long left the building (the company).&lt;/p&gt;  &lt;p&gt;OK, I&amp;#39;m sorry since this wasn&amp;#39;t supposed to be yet-another-ranting about the perils of maintaining old proprietary code. When I was asked to create a new handful of import tools I was determent to make them easier to maintain. What I wanted to do was to create a number of classes that contained all method necessary to do the importing regardless of the data source. All information about the file and where it should be imported to would be in a config file, easily editable without the need to open up the source code. When all this was done, I educated my colleague, that are responsible for monitoring these tools, on how to use them. He isn&amp;#39;t a developer but he does know some VB, and using the framework I created he&amp;#39;s been able to create new import tools by himself.&lt;/p&gt;  &lt;p&gt;What I didn&amp;#39;t realize then was how the number of necessary imports was going to grow, and grow fast. The problem is that I never created one program that could use different config files at different points in time. So the program had to be copied, sometimes changed a bit for a new customer import, and deployed as a new EXE file that run on the server at scheduled points in time. I think that we now have around 40 almost identical programs running on the server. So now they are also starting to be a mess to administrate and maintain. So it&amp;#39;s pretty obvious that something needs to be done.&lt;/p&gt;  &lt;p&gt;Already two years ago when I first created this import tool framework I was thinking that this should really be done with some clever scripting language. Whenever a change is made in the file that we need to import we could simply open up the script in any text editor make the necessary changes and save it again without the need to launch Visual Studio on my local machine, check out the source code from our &lt;a href="http://www.sourcegear.com/vault/" target="_blank"&gt;source control system&lt;/a&gt;, make the changes, test, build and compile it, check in the source and deploy the application on the server. Well, several choices are available. I could have used cool stuff like Python or even PHP but, no that would require their runtimes to be deployed on the server... Not a biggy you say, well it is when it&amp;#39;s going up on a live environment. It have to be lab tested first, with all kind of rigorous evaluation, new documentation have to be written and approved and all that. There was just not time for anything like that since our customer wanted the information yesterday. What we already have on the server is the .NET framework, live with it chump. OK, so how about scripting for .NET then? Well, we always have &lt;a href="http://msdn.microsoft.com/en-us/library/ms974548.aspx" target="_blank"&gt;VSA&lt;/a&gt; (Visual Studio for Applications) but that requires a lot of work. Another quicker solution is to use System.CodeDom and reflection, so that&amp;#39;s the path I choose.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Creating the contract&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The System.CodeDom.Compiler namespace contains the abstract class &lt;a href="http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.aspx" target="_blank"&gt;CodeDomProvider&lt;/a&gt; from which language specific providers inherits. In my case I&amp;#39;m interested in the &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.vbcodeprovider.aspx" target="_blank"&gt;VbCodeProvider&lt;/a&gt; which resides in the Microsoft.VisualBasic namespace, you could also use the &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx" target="_blank"&gt;CSharpCodeProvider&lt;/a&gt; if you wish. The namespace also contains the &lt;a href="http://msdn.microsoft.com/en-us/library/system.codedom.compiler.compilerparameters.aspx" target="_blank"&gt;CompilerParameters&lt;/a&gt; class with which you would set, yes you guessed it, parameters necessary to compile the source into an assembly. I will get to these classes shortly, but first let’s have a look at how we would create a contract between a script file and the scripting engine that we will use to run the script. To keep it as simple as possible I created a new Class library project which I named Scripting. This library only contain an Interface with one single method called ScriptMain() which all scripts must implement.&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Public Interface &lt;/span&gt;IScriptable
  &lt;span style="color:blue;"&gt;Sub &lt;/span&gt;ScriptMain()
&lt;span style="color:blue;"&gt;End Interface&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;With that in place a minimal script that does absolutely nothing would look like this:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Public Class &lt;/span&gt;MyScript
  &lt;span style="color:blue;"&gt;Implements &lt;/span&gt;Scripting.IScriptable

  &lt;span style="color:blue;"&gt;Public Sub &lt;/span&gt;ScriptMain() &lt;span style="color:blue;"&gt;Implements &lt;/span&gt;Scripting.IScriptable.ScriptMain
    &lt;span style="color:green;"&gt;&amp;#39;script code goes here
  &lt;/span&gt;&lt;span style="color:blue;"&gt;End Sub
End Class&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;strong&gt;Using the CodeDom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now for the fun part. How to run the script. For the sake of this article I’m going to create a small demo application, consisting of a Windows Form with one button and a multi-line textbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joacim.metablogapi/6886.image_5F00_07E83C19.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joacim.metablogapi/7041.image_5F00_thumb_5F00_459523D5.png" width="244" height="130" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You would write the script in the textbox and hit the button to run it. To this project I also add a class we will name ScriptEngine. I also added a reference to the above mentioned class library containing our interface. The ScriptEngine class will import the System.CodeDom.Compiler and the System.Reflection namespaces plus our Scripting namespace containing the IScriptable interface.&lt;/p&gt;

&lt;p&gt;As I mentioned earlier I’m going to use the VBCodeProvider to compile the script. By default the provider will compile the source to a .Net 2.0 assembly. To be able to use version 3.5 of the .Net framework we need to supply some provider options to the constructor of the class.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Dim &lt;/span&gt;providerOptions = &lt;span style="color:blue;"&gt;New &lt;/span&gt;Collections.Generic.Dictionary(&lt;span style="color:blue;"&gt;Of String&lt;/span&gt;, &lt;span style="color:blue;"&gt;String&lt;/span&gt;)
providerOptions.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;CompilerVersion&amp;quot;&lt;/span&gt;, &lt;span style="color:#a31515;"&gt;&amp;quot;v3.5&amp;quot;&lt;/span&gt;)
&lt;span style="color:blue;"&gt;Dim &lt;/span&gt;provider &lt;span style="color:blue;"&gt;As New &lt;/span&gt;VBCodeProvider(providerOptions)&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Now that we have our code provider we must also add some parameters. The compiler parameters tell the provider how we want our assembly to be built. In our case we do not want to create an executable file but instead just create an in memory assembly. We also want to add references to some other commonly used assemblies like System.Windows.Forms and others, most importantly we add a reference to our Scripting.dll which contains the IScriptable interface. To be able to add this class library it either have to be added to the GAC or exist in the same folder as our executable (I use the latter). We can also provide some compiler options like Option Explicit, note that the /OptionInfer flag can only be used if we use the v3.5 Framework compiler.&lt;/p&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Dim &lt;/span&gt;parameters &lt;span style="color:blue;"&gt;As New &lt;/span&gt;CompilerParameters
&lt;span style="color:blue;"&gt;With &lt;/span&gt;parameters
  .GenerateExecutable = &lt;span style="color:blue;"&gt;False
  &lt;/span&gt;.GenerateInMemory = &lt;span style="color:blue;"&gt;True
  &lt;/span&gt;.IncludeDebugInformation = &lt;span style="color:blue;"&gt;False
  &lt;/span&gt;.ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;System.dll&amp;quot;&lt;/span&gt;)
  .ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;System.Windows.Forms.dll&amp;quot;&lt;/span&gt;)
  .ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;Microsoft.VisualBasic.dll&amp;quot;&lt;/span&gt;)
  .ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;Scripting.dll&amp;quot;&lt;/span&gt;)
  .CompilerOptions = &lt;span style="color:#a31515;"&gt;&amp;quot;/OptionExplicit+ /OptionStrict- /OptionInfer+&amp;quot;
&lt;/span&gt;&lt;span style="color:blue;"&gt;End With&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;OK, we now have everything set up to compile an assembly out of our script. The CodeDom provider have a number of methods to do this, for example CompileAssemblyFromFile() or CompileAssemblyFromSource(). In this demo we don’t have any source file stored on our hard drive so we will use the latter of these two methods that accepts a string as the source. As a matter of fact it actually accepts a ParamArray of strings if we would have several sources of code that we wanted to compile into the same assembly.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Dim &lt;/span&gt;result &lt;span style="color:blue;"&gt;As &lt;/span&gt;CompilerResults
result = provider.CompileAssemblyFromSource(parameters, source)&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;As the first argument we pass the CompilerParameters we just created and then the string containing our source code. If the returned CompilerResults doesn’t contain any compiler errors we are ready to run our script.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;If Not &lt;/span&gt;result.Errors.HasErrors&lt;span style="color:blue;"&gt; Then
  Dim &lt;/span&gt;script &lt;span style="color:blue;"&gt;As &lt;/span&gt;IScriptable = FindScriptable(result.CompiledAssembly)
  &lt;span style="color:blue;"&gt;If &lt;/span&gt;script &lt;span style="color:blue;"&gt;IsNot Nothing Then
    &lt;/span&gt;script.ScriptMain()
&lt;span style="color:blue;"&gt;  End If
End If&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Here we call a little helper function named FindScriptable() that uses reflection to find a reference to our IScriptable interface that our script had to implement. If found we simply call the ScriptMain() method to run our script.&lt;/p&gt;

&lt;p&gt;The full source code for our script engine will look as follows:&lt;/p&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Imports &lt;/span&gt;System.CodeDom.Compiler
&lt;span style="color:blue;"&gt;Imports &lt;/span&gt;System.Reflection
&lt;span style="color:blue;"&gt;Imports &lt;/span&gt;Scripting&lt;/pre&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Public Class &lt;/span&gt;ScriptEngine
  &lt;span style="color:blue;"&gt;Public Function &lt;/span&gt;RunScript(&lt;span style="color:blue;"&gt;ByVal &lt;/span&gt;source &lt;span style="color:blue;"&gt;As String&lt;/span&gt;) &lt;span style="color:blue;"&gt;As Boolean
    Dim &lt;/span&gt;providerOptions = &lt;span style="color:blue;"&gt;New &lt;/span&gt;Collections.Generic.Dictionary(&lt;span style="color:blue;"&gt;Of String&lt;/span&gt;, &lt;span style="color:blue;"&gt;String&lt;/span&gt;)
    providerOptions.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;CompilerVersion&amp;quot;&lt;/span&gt;, &lt;span style="color:#a31515;"&gt;&amp;quot;v3.5&amp;quot;&lt;/span&gt;)
    &lt;span style="color:blue;"&gt;Dim &lt;/span&gt;provider &lt;span style="color:blue;"&gt;As New &lt;/span&gt;VBCodeProvider(providerOptions)
    &lt;span style="color:blue;"&gt;Dim &lt;/span&gt;parameters &lt;span style="color:blue;"&gt;As New &lt;/span&gt;CompilerParameters
    &lt;span style="color:blue;"&gt;With &lt;/span&gt;parameters
      .GenerateExecutable = &lt;span style="color:blue;"&gt;False
      &lt;/span&gt;.GenerateInMemory = &lt;span style="color:blue;"&gt;True
      &lt;/span&gt;.IncludeDebugInformation = &lt;span style="color:blue;"&gt;True
      &lt;/span&gt;.ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;System.dll&amp;quot;&lt;/span&gt;)
      .ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;System.Windows.Forms.dll&amp;quot;&lt;/span&gt;)
      .ReferencedAssemblies.Add(&lt;span style="color:#a31515;"&gt;&amp;quot;Microsoft.VisualBasic.dll&amp;quot;&lt;/span&gt;)
      .CompilerOptions = &lt;span style="color:#a31515;"&gt;&amp;quot;/OptionExplicit+ /OptionStrict- /OptionInfer+&amp;quot;
    &lt;/span&gt;&lt;span style="color:blue;"&gt;End With
    Dim &lt;/span&gt;result &lt;span style="color:blue;"&gt;As &lt;/span&gt;CompilerResults
    result = provider.CompileAssemblyFromSource(parameters, source)
    &lt;span style="color:blue;"&gt;If Not &lt;/span&gt;result.Errors.HasErrors&lt;span style="color:blue;"&gt; Then
      Dim &lt;/span&gt;script &lt;span style="color:blue;"&gt;As &lt;/span&gt;IScriptable = FindScriptable(result.CompiledAssembly)
      &lt;span style="color:blue;"&gt;If &lt;/span&gt;script &lt;span style="color:blue;"&gt;IsNot Nothing Then
        &lt;/span&gt;script.ScriptMain()
        &lt;span style="color:blue;"&gt;Return True
      Else
        Return False&lt;br /&gt;      End If
    Else
      Return False
    End If
  End Function

  Private Function &lt;/span&gt;FindScriptable(&lt;span style="color:blue;"&gt;ByVal &lt;/span&gt;assembly &lt;span style="color:blue;"&gt;As &lt;/span&gt;Assembly) &lt;span style="color:blue;"&gt;As &lt;/span&gt;IScriptable
    &lt;span style="color:blue;"&gt;For Each &lt;/span&gt;t &lt;span style="color:blue;"&gt;As &lt;/span&gt;Type &lt;span style="color:blue;"&gt;In &lt;/span&gt;assembly.GetTypes()
      &lt;span style="color:blue;"&gt;If &lt;/span&gt;t.GetInterface(&lt;span style="color:#a31515;"&gt;&amp;quot;IScriptable&amp;quot;&lt;/span&gt;, &lt;span style="color:blue;"&gt;True&lt;/span&gt;) &lt;span style="color:blue;"&gt;IsNot Nothing Then
        Return DirectCast&lt;/span&gt;(assembly.CreateInstance(t.FullName), IScriptable)
      &lt;span style="color:blue;"&gt;End If
    Next
    Return Nothing
  End Function
End Class&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;font color="#0000ff" face="Courier New"&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;font color="#0000ff" face="Courier New"&gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Now all that is left is to call the RunScript() method from our little editor. So in the Click event of the button I have the following code:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Dim &lt;/span&gt;engine &lt;span style="color:blue;"&gt;As New &lt;/span&gt;ScriptEngine
engine.RunScript(TextBox1.Text)&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;In this simple demo the RunScript() method simply return true or false on success or failure. In a real application you probably want to get more information why the compilation of your script failed. In that case you can let the method return a CompilerErrorCollection instead. You get this collection from the CompilerResults class.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;Return &lt;/span&gt;result.Errors&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;You can then loop through this collection and give information about each compiler error including the error message and the line the error occurred on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extending the script engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You now probably want to add your own classes to the script engine. The easiest way of doing that is just to add your classes to the Scripting class library since we already add a reference to that in the script. I only used shared methods in my classes just to make the scripts easier to create, but you could do as you wish. To keep my scripts tidy of any extra “noise” I also simply required that the script contained a public sub called ScriptMain() so that it doesn’t even have the Class or the Implement IScriptable declaration in the script file. I simply add that to the source string before I pass it along to the CodeDomProvider.&lt;/p&gt;

&lt;p&gt;I also used the ICSharpCode.TextEditor that is part of the &lt;a href="http://www.icsharpcode.com/OpenSource/SD/Default.aspx" target="_blank"&gt;SharpDevelop&lt;/a&gt; project to build my own custom IDE with syntax highlighting.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joacim.metablogapi/2677.image_5F00_15F70549.png"&gt;&lt;img style="border-right-width:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" title="image" border="0" alt="image" src="http://msmvps.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/joacim.metablogapi/4214.image_5F00_thumb_5F00_059B8D90.png" width="604" height="411" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy scripting!&lt;/p&gt;
&lt;a href="http://pimpthisblog.com/Using-the-CodeDom-to-do-scripting-with-Net-Joacims-view-on-stuff" rev="vote-for"&gt;&lt;img style="border-right-width:0px;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;" alt="pimp it" src="http://pimpthisblog.com/image.axd?url=http%3A%2F%2Fmsmvps.com%2Fblogs%2Fjoacim%2Farchive%2F2009%2F06%2F16%2Fusing-the-codedom-to-do-scripting-with-net.aspx" /&gt;&lt;/a&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fmsmvps.com%2fblogs%2fjoacim%2farchive%2f2009%2f06%2f16%2fusing-the-codedom-to-do-scripting-with-net.aspx"&gt;&lt;img border="0" alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fmsmvps.com%2fblogs%2fjoacim%2farchive%2f2009%2f06%2f16%2fusing-the-codedom-to-do-scripting-with-net.aspx" /&gt;&lt;/a&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://msmvps.com/aggbug.aspx?PostID=1695554" width="1" height="1"&gt;</description><category domain="http://msmvps.com/blogs/joacim/archive/tags/vb/default.aspx">vb</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/visual+basic/default.aspx">visual basic</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/scripting/default.aspx">scripting</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/script/default.aspx">script</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/codedom/default.aspx">codedom</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/reflection/default.aspx">reflection</category><category domain="http://msmvps.com/blogs/joacim/archive/tags/.net/default.aspx">.net</category></item></channel></rss>