Saturday, January 28, 2006

Unsealing Visual Studio Generated Properties.Settings

I've often wanted to have public access to the Properties.Settings classes generated by Visual Studio 2005 because:
  1. I want to inherit from the Settings to add special helper methods to the generated code.
  2. I want to write external unit tests that access the Properties.Settings, but they are internal.
  3. I also want to inherit from Setting to add a buffer layer between my code and the generated code. In case I need to fix things later, when Microsoft changes the generator.

At first, I thought: There's no real good way to do the above without manually editing generated code.

But then I learned more about the "Custom Tool" property that shows when you click F4 (Properties) on the Application ... Properties... Settings.settings node in the project tree. By default this will say: "SettingsSingleFileGenerator"

What is "SettingsSingleFileGenerator"? It's a .NET object that exposes a special COM interface. The .NET object runs and generates code (from XML settings in this case), which you see in the generated code node underneath: Settings.Designer.cs.

Turns out you can make your own generators and then install them into visual studio. But, it would be a pain to re-create the SettingsSingleFileGenerator. Luckily, you can chain these generators together: You just set the CustomTool properties of the previously generated code to use your custom generator. Nice!

Like in this screen shot:





If the instructions in the two links above make sense, then here is the code for a generator that will unseal your settings.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

using CustomToolGenerator;

// Code downloaded from here: http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=4AA14341-24D5-45AB-AB18-B72351D0371C
// Install notes: http://www.drewnoakes.com/snippets/WritingACustomCodeGeneratorToolForVisualStudio/
namespace UnsealedSettingsGenerator
{
[Guid("828BA458-f1f1-f1f1-f1f1-91ACEC2F38FD")]
public class UnsealSettingsGenerator: BaseCodeGeneratorWithSite
{

protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
{
// Replace the source from the input, with our new text
// This is lamer than parsing, but it does the job and makes the point.
inputFileContent = inputFileContent.Replace("internal sealed", "public");
return Encoding.ASCII.GetBytes(inputFileContent);
}

public override string GetDefaultExtension()
{
return ".Unsealed" + base.GetDefaultExtension();
}
}
}

A key point is to make sure you set the registry keys right (see screen below), so VS2005 will load your generator. Also, you can right click on the source file and choose "Run Custom Tool" to force your generator to run.



This is all a lot of work to generate and install. I have some code that lets create your own generators easily and keep the "generator's code" (not the generated code) in the same project you're running in (without all the COM malarkey). Maybe I'll upload that sometime.