Posted on December 31st, 2006

After seeing Ron’s post, and really having nothing better to do on New Year’s Eve, I discovered that I agree there must be a bug in this test:

I am nerdier than 99% of all people. Are you nerdier? Click here to find out!

If only I had used Lynx to take the test!

No Comments


Posted on December 13th, 2006

In my last post, I talked about the new XslCompiledTransform class in .NET 2.0. It’s very cool, and seems to be very fast once it is loaded. There’s just problem.

What if you have more than one stylesheet?

I have a customer who builds their site using XML->HTML XSL Transformations, and they have around 200 stylesheets. We could spin up 200 instances of XslCompiledTransform, but that just seems silly. After all, since all the class is doing is compiling the XSL sheets to IL, we surely should be able to do that at compile time, or at least at run time and pass the compiled sheet in. Right?

Well, it doesn’t seem that way. My first clue was that XslCompiledTransform was sealed. That usually means that some juicy thing is in there that would be really, really, really useful if it wasn’t sealed. And in this case, that’s the truth. Because it has two methods that seem like they could be useful – CompileToQil, and CompileIlFromQil. The latter of which returns an XmlCommand, which Transform just calls Execute on.

Juicy methods!

So it seems that if we could create the XmlCommand ourselves, we could just pass those in and have XslCompiledTransform execute them. Again, not the case, because all of those methods, and the underling object structure (from System.Data.SqlXml) are all either internal or sealed. But, they don’t have access security around them, so let’s see what a little reflection can get us.

First, the path seems to be that on Load, we compile the XSL to QIL, which we can then compile down to IL from. So, my first goal was to see if I could get CompileToQil working. Using Lutz Roeder’s handy-dandy reflector, I see that CompileToQil is local to XslCompiledTransform, and takes a stylesheet, some XsltSettings, and a stylesheetResolver. Luckily, I can see that the code has some defaults, so let’s see what happens. First, we need to load the Assembly:

Assembly sqlXml = Assembly.LoadFrom(@"C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.SqlXml.dll");

Next, I notice that CompileToQil is actually doing:

this.compilerResults = new System.Xml.Xsl.Xslt.Compiler(settings).Compile(stylesheet, stylesheetResolver, out this.qil);

So rather than spin up an XslCompiledTransform, let’s see if we can just create a Compiler and call Compile. First, we need to create the type and spin up an instance:

Type typClsCompiler = sqlXml.GetType("System.Xml.Xsl.Xslt.Compiler", true);
object oClsCompiler = Activator.CreateInstance(typClsCompiler, new Object[] { XsltSettings.Default }, null);

I used XsltSettings.Default, as that is what XslCompiledTransform used if it wasn’t passed in. Next, we’ll create a method info, and invoke it. Except…the last parameter in Reflector shows that QIL is returned as an out parameter:

Out Param?

I’ve done some reflection, but I’ve never had to deal with out params in an invoked method. Luckily, a man who is a camera had just the thing, and showed me the way, leading to this:

// set the third parameter modifier value to true to
// mark it as being an out parameter
ParameterModifier[] arrPmods = new ParameterModifier[1];
arrPmods[0] = new ParameterModifier(3);
arrPmods[0][0] = false; // not out
arrPmods[0][1] = false; // not out
arrPmods[0][2] = true; // out

// define the types of parameters in the target signature
// use a reference type for the out parameter
System.Type[] arrTypes = new System.Type[3];
arrTypes.SetValue(Type.GetType("System.Object"), 0);
arrTypes.SetValue(systemXml.GetType("System.Xml.XmlResolver"), 1);
arrTypes.SetValue(sqlXml.GetType("System.Xml.Xsl.Qil.QilExpression&"), 2);
MethodInfo miCompile = typClsCompiler.GetMethod("Compile", arrTypes, arrPmods);

// package our parameters
object[] arrParms = new object[3];
arrParms.SetValue(stylesheet, 0);
arrParms.SetValue(stylesheetResolver, 1);
// no need to set the third parameter -- it will be filled in
object results = miCompile.Invoke(oClsCompiler, arrParms);
// extract the value from the out parameter
object qilObject = arrParms[2];
Console.WriteLine("qilObject null? " + (qilObject == null));

Which, when run, promptly printed qilObject null? false!

Got QIL!

I don’t really believe it could be that easy, since it looks like I’m halfway to my goal. Just to confirm, I set a breakpoint and look at what qilObject really is:

It really is QIL

Which gives me proof that I do indeed have a QIL object. So, with that out of the way, let’s see if we can get it compiled to IL. Looking back at Reflector, specifically the CompileIlFromQil method, I see that to create the XmlCommand, we create an XmlILGenerator object and call Generate, passing in the QIL and the AssemblyName of the XsltSettings. Since we already have both of those, let’s see if we can just create that ourselves. First, I verify that XmlILGenerator is indeed internal:

It's internal, alright

So, let’s set up some reflection calls:

Type typClsXmlILGenerator = sqlXml.GetType("System.Xml.Xsl.XmlILGenerator", true);
object oClsXmlILGenerator = Activator.CreateInstance(typClsXmlILGenerator);

//We've now got the QIL. Let's compile it to IL
MethodInfo miGenerate = typClsXmlILGenerator.GetMethod("Generate");
object xmlCommand = miGenerate.Invoke(oClsXmlILGenerator,
  new Object[] { qilObject, XsltSettings.Default.GetType().Assembly.GetName() });

Console.WriteLine("xmlCommand null? " + (xmlCommand == null));

which is much cleaner, because we aren’t dealing with out parameters. Notice the call to get the Assembly name for XsltSettings – in Reflector it shows a call to an AssemblyName property of XsltSettings, but I don’t have access to that here, so I’m just jumping to reflection to get it. So, now, we should have the IL generated and be ready for the next steps to inject it into XslCompiledTransform. First, let’s just run it to make sure:

Lots o' errors

Well then. A COMException in interop? Perhaps not what I was expecting, and probably won’t be easy to track down. I’m sure I can dive into it and see what comes out, but I’ve been at this for a couple of hours now, and think that this is a good stopping point.

So, what have we learned? It’s a shame that we have all of these internal and sealed classes, because the ability to precompile the XSL sheets seems like a very useful utility. We have some fallbacks, including create static members for each stylesheet, or creating a lazy loaded lookup table for XslCompiledTransform instances, but I’m going to keep playing with this to see how far it gets me.

4 Comments


Posted on December 12th, 2006

In .NET 2.0, people may have noticed that the new recommended way of doing XSL transformations is now System.Xml.Xsl.XslCompiledTransform. And they may be tempted to do something like the following:

public string Transform(string xml, string xsl)
{
  System.IO.StringWriter writer = new System.IO.StringWriter();
  System.Xml.Xsl.XslCompiledTransform t =
    new System.Xml.Xsl.XslCompiledTransform();
  XmlReaderSettings settings = new XmlReaderSettings();
  XmlReader xmlReader = XmlReader.Create(new System.IO.StringReader(xml));
  XmlReader xslReader = XmlReader.Create(new System.IO.StringReader(xsl));
  t.Load(xslReader);
  t.Transform(xmlReader, null, writer);
  return writer.ToString();
}

which is all well and good for on the fly. But there is something tricky going on here. XslCompiledTransform does more than just transform. It compiles the XSLT to MSIL code.

What does this mean? Well, that doing the above is like JITing your ASPX pages – you are incurring the overhead of the transformation and the compilation. Since most people really don’t need to pass in the XSL, you can greatly improve performance by caching the XSL object, or making it static, ala:

private static string myXsl = "";
private static System.Xml.Xsl.XslCompiledTransform transformer =
  new System.Xml.Xsl.XslCompiledTransform();

static Transform
{
  XmlReader xslReader = XmlReader.Create(new System.IO.StringReader(xsl));
  transformer.Load(xslReader);
}

In a test, running the first version 1000 times took about 5 seconds. In the second, it took less than a second.

2 Comments


Posted on December 5th, 2006

This morning on Slashdot they have a story about CIOs’ First Impressions about Vista. Similar to other stories, they focus on how long companies are waiting to roll out Vista.

The problem is that this isn’t unique to Vista – or Microsoft. I’ve worked for companies doing Java, Ruby, ColdFusion, ASP and .NET. We’ve had Windows, Unix, Linux and other systems in the front and back end. Every time a new version came out of just about anything (CF5/6/MX, .NET 2.0/3.0, Eclipse, VS 2k5, Java 5, etc) the devs in the company were desperate to start running it. Even with operating systems – I was in a shop when Windows 2k came out, and we wanted nothing more than to upgrade to it. But, especially in the larger companies, we had policies, and committees, and indeed entire departments devoted to “evaluating” and developing a “deployment schedule”. Guess what – any time you see those phrases together, you can count on a year deployment.

In other words, the spin that companies are deploying Vista simply because it is Microsoft is a bit of a farce. All the arguments that people are happy running NT 4.0 can easily be countered with many people still running the 2.2 kernel. People don’t like change, and that’s something that can be universally, company-agnostically applied.

And to those with comments like “your [sic] an employee at Microsoft and you haven’t had a chance to use it yourself?” I can assure you that they push us hard, and provide a lot of the resources necessary, to become familiar with the software. I’m running Vista on my laptop, and so far I like it – even though I didn’t think I would. In fact, the local offices have been setting up install-fests, just like we used to have with some of the LUGs I’ve been part of, to help people get upgraded.

2 Comments


Posted on December 4th, 2006

On my recent post about Finding Team Explorer someone asked about how to get it when all they have is the VSTD (Visual Studio Team Developer) DVD.

Basically Visual Studio shipped before TFS, so the client isn’t on those DVDs. However, you can get it from our download center. CodePlex, Microsoft’s new code sharing site, uses TFS as the source control, so they have a good set of instructions for downloading, extracting, and installing the client.

(Thanks to Brian Harry for pointing out the CodePlex install instructions)

No Comments