Issues with the XmlSerializer in medium trust environments

In my last post I briefly mentioned that CodeHTMLer had issues running in a medium trust environment. The simple web application front end I created kept failing before it ever got started. It keep hitting a SecurityException nested in an InvalidOperationException which looks something like:

[Exception: System.InvalidOperationException: There was an error reflecting type 'CodeHtmler.Languages'. ---> System.Security.SecurityException: Request failed.
   at System.Reflection.CustomAttribute._CreateCaObject(...)
   at System.Reflection.CustomAttribute.CreateCaObject(...)
   at System.Reflection.CustomAttribute.GetCustomAttributes(...)
   at System.Reflection.CustomAttribute.GetCustomAttributes(...)
   at System.Reflection.RuntimePropertyInfo.GetCustomAttributes(...)
   at System.Xml.Serialization.XmlAttributes..ctor(...)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(...)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(...)
The action that failed was:
InheritanceDemand
The type of the first permission that failed was:
System.Security.PermissionSet
The Zone of the assembly that failed was:
MyComputer

CodeHTMLer uses the XmlSerializer to read and write the language definitions and it appeared to be what was causing this exceptions to be thrown. I spent a good deal of time trying to figure out why this wasn't working because every thing I read online said the XmlSerializer should actually work in medium trust. After a while of debugging I finally figured out that the problem was caused by some custom attributes.

[XmlAttribute]
[Editor(typeof(HtmlColorEditor), typeof(System.Drawing.Design.UITypeEditor))]
public string Color {...}

Apparently when the XmlSerializer is walking through the custom attributes it runs into the EditorAttribute and then tries to get the type of my custom editor HtmlColorEditor which has UITypeEditor in it's inheritance chain. However UITypeEditor for some reason demands a FullTrust permission set for Inheritance, which is problematic in a medium trust environment. To verify for certain that this was the issue I removed those editor attributes and then the XML serialization worked as expected under medium trust.

Those editor attributes are used by the PropertyGrid, which is my lame way of providing UI to edit the language definitions, so I didn't want to remove them permanently but I also wanted the serialization to work in a medium trust environment. To allow for both the PropertyGrid and the medium trust serialization to work I decided to use the sgen tool to generate the serialization classes statically when I knew I had FullTrust permissions, instead of dynamically at runtime like the generic XmlSerializer class does.

The output of sgen was another assembly, CodeHtmler.XmlSerializers.dll which I then needed to reference and switch the code from using the more general XmlSerializer(typeof(Languages)) to the specific class  LanguagesSerializer(). This was cool because it allowed me to accomplish what I wanted but it in turn forced me to now release two separate assemblies, CodeHtmler.dll and CodeHtmler.XmlSerializers.dll. Wouldn't it be nice if I could get away with just one assembly. It turns out that sgen has this /keep option that will tell it not to delete the temp code files used to build the new assembly. This was great because now I can just take the generated source code for the LanguagesSerializer class and build it as part of my CodeHtmler.dll assembly. Here is the command line I used to do this:

sgen CodeHtmler.dll /keep /force /type:CodeHtmler.Languages

There is a little bit of the chicken and egg problem here because I needed the CodeHtmler.dll to generate the serialization code and I needed serialization code to build CodeHtmler.dll. It really wasn't that big of an issue though because the serialization doesn't actually need to work to run sgen it just needs to be stubbed out so CodeHtmler.dll can be built.

This approach works quite nicely. Now the only issue is that I have to remember to regenerate the serialization code whenever I change any of my serialized classes. Fortunately, I only have a small number of classes I serialize and their serialization interface doesn't change much so I think I can live with this approach.

No Comments