Visual Studio .NET Code Generator Shim

Copyright © 2003 Atif Aziz, Skybow AG

This documentation applies to Visual Studio Code Generator Shim v1.0.4306.0 (BETA).

Visit the GotDotNet Workspace of this project.

The VS Code Generator Shim is a generic custom tool for Visual Studio .NET 2002 and 2003 that enables code generators to be written easily and quickly in just about any .NET language and using familiar .NET Framework constructs. A standard custom tool designed for Visual Studio must use COM interoperability (when written in managed code) and register with the IDE to be fully operational, but the shim helps eliminates these requirements entirely. You only need to create a class and expose a single method to write a code generator now. What is more, the code generator can be completely idependent of Visual Studio releases.

Introduction

Visual Studio .NET 2002 and 2003 allow code generators to be written for files in a project. A code generator is responsible for taking an input file and transforming it into source code in the language of the project. In Visual Studio .NET terminology, these code generators are called custom tools. A good example of a custom tool is the MSDataSetGenerator that ships with Visual Studio .NET. It takes an XSD file as its input and uses the schema described within to generate a strongly-typed DataSet class in either C# or VB .NET (or whatever happens to be language of the project). To associate a custom tool with any file in a project, you simply activate the Property window for the file and set the tool's friendly name into the Custom Tool property.

Custom tools are fundamentally COM objects that implement the IVsSingleFileGenerator interface and therefore can be written in a .NET language that supports COM Interop. A custom tool must be registered as a COM compontent in the system as well as a code generator with Visual Studio .NET.

The purpose of the VS Code Generator Shim is to simplify development of code generators for Visual Studio .NET by providing a completely managed solution that does not require COM Interop or registration with Visual Studio .NET. To achieve this, the VS Code Generator Shim simply registers itself as the COM object and the custom tool with the IDE and then delegates the real work to a downstream managed code generator. The only requirement is that the input file must be in XML.

Using the Shim

To tell the VS Code Generator Shim (referred to simply as shim from this point forth) which downstream code generator to invoke, the input file must be in XML and contain the following XML processing instruction:

<?codegen type="..."?>

The codegen processing instruction must appear somewhere before the root element in the XML document, otherwise the shim throws an exception. And although it can be specified more than once in the same document, only the first one is ever used.

The type parameter of the codegen processing instruction specifies the assembly-qualified type name of the code generator to use. If the assembly containing the code generator does not have a strong-name or it is not installed in the Global Assembly Cache (GAC) , then you can omit the assembly specification from the type parameter and instead provide the fully-qualified path of the assembly file in a separate parameter called codeBase. The downside of this, however, is that the path gets hard-wired into the XML file. The shim therefore allows the use environment paths. This is done leaving just the assembly file name and extension in the codeBase and specifying an additional boolean parameter on the processing instruction called usePath. If the value of usePath is 1 or true (instead of 0 or false; the deftault) then the shim will try to locate the assembly by appending its files name to the list of semi-colon delimited paths in the CODEGENPATH environment variable.

Once the assembly is successfully loaded, the shim goes on to instaniate an instance of the type. It then checks if the downstream code generator supports the Skybow.CodeGeneration.ICodeGenerator interface. If it does, then it calls its sole Generate method, passing in a context, a text reader to parse the input and a text writer to write the code.

The ICodeGenerator interface is defined as follows:

C#
public interface ICodeGenerator
{
void Generate(
    IContext context, 
    TextReader inputReader, 
    TextWriter outputWriter);
}
Visual Basic
Public Interface ICodeGenerator

Sub Generate( _
    ByVal context As IContext, _
    ByVal inputReader As  TextReader, _ 
    ByVal outputWriter As TextWriter)

End Interface

If the above interface is not implemented by the code generator, then the shim uses late-binding. This latter scenario is designed to favor developers who wish to write code generators with minimum dependencies. Implementing Skybow.CodeGeneration.ICodeGenerator adds a dependency on the SkybowVsCodeGenerator assembly. When late-binding, the shim looks for a public method called Generate that has the following signature:

C#
public void Generate(
IDictionary contextProperties, 
TextReader inputReader, 
TextWriter outputWriter)
Visual Basic
Public Sub Generate( _
ByVal contextProperties As IDictionary, _
ByVal inputReader As TextReader, _
ByVal outputWriter As TextWriter)

If the method is called differently, you can specify it using the entryPoint parameter on the codegen instruction.

To use the code generator from Visual Studio .NET, the input XML file must be associated with the Skybow.CodeGen custom tool. Given this, writing a custom tool or code generator is now as simple as creating a class that exposes the Generate method (or alternatively implementing Skybow.CodeGeneration.ICodeGenerator).

The parameters of the Generate method are as follows:

Generate method parameters
Parameter Type Description
contextProperties IDictionary A name-value pair dictionary of context properties for the code generator.
context IContext A name-value pair dictionary of context properties for the code generator. The Properties property contains the actual dictionary whereas the context parameter itself is the context object.
inputReader TextReader The reader object for reading the input source text.
outputWriter TextWriter The writer object to be used by the generator to write the output source code text.

The context properties dictionary is filled in by the shim for the code generator in case it wishes to use any. The currently set of defined context properties are show here:

Context properties
Property (Key) Description
urn:schemas-skybow-com:codegen:DefaultNamespace A string containing that contains the default namespace associated with the Visual Studio project.
urn:schemas-skybow-com:codegen:InputFilePath A string containing that specifies the path to the input file.
urn:schemas-skybow-com:codegen:Language A string that specifies the target language in which the code should be generated. The currently recognized values are: C#, VB and VJ#. This string is case-sensitive.

To be completed ...

Samples

There are 5 sample code generators that ship with the shim, each showing a different technique to leverage code generation:

HelloCodeGenerator
A simple code generator that creates a class with a single method that calls Console.WriteLine to output a message. The text of the message is configurable and is taken from the input XML file. This generator uses the CodeDom technology to produce the code.
WebServiceClientGenerator
This code generator does essentially the same as the WSDL tool that ships with the .NET Framework SDK. It uses the ServiceDescriptionImporter class from the .NET Framework to do the real work. The location of the WSDL file for which to generate the client proxy code is taken from the input XML file.
XsdClassesGenerator
This code generator demonstrates how to call an external program to generate the code. The input XML is assumed to be an XML Schema definition. To produce the final code, then code generator launches the XML Schema Definition Tool (XSD.EXE) from the .NET Framework SDK and therefore requires it to be installed. The output from XSD.EXE is piped back to the Visual Studio output window or whatever happens to be the shell.
XsdDataSetGenerator
This code generator does the same as XsdClassesGenerator, except instead of passing the /classes switch to XSD.EXE, it passes /dataset to generate a typed DataSet class.
SqlCodeGenerator
This code generator uses SQL statements to generate code.

All of the code generators can be run in Microsoft Visual Studio .NET 2002 or 2003.

Additional References

See discussion of Custom Tools in Top Ten Cool Features of Visual Studio .NET Help You Go From Geek to Guru.