What is a Custom Tool?
Here are some statements which describe a custom tool:
It’s a file generator
It makes code-behind files
It extends Visual Studio
It’s stored in a ComVisible DLL file
It uses the Registry
Quoted from ‘Custom Tools Explained‘
Steps to create custom tool
- Create Dll project for custom tool (AKA: Repository.CodeGenerator).
- Add a new ‘RepositoryCodeGenerator.reg’ file with the below content.
- Add your ‘Repository.xsd’ XSD file.
- Add your new ‘Generator.cs’ class file (see the content of ‘Generator.cs’).
- Make sure your project is COM visible.
[csharp title=”<h4>Content of Generator.cs</h4>”]
using Microsoft.VisualStudio.TextTemplating.VSHost;
using System.Runtime.InteropServices;
namespace Repository.CodeGenerator
{
[Guid("11111111-1111-1111-1111-111111111111")]
public class Generator : BaseCodeGenerator
{
protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
{
return new MyRepositoryBuilder(MyGeneratorErrorCallback).MyGenerateRepositoryCode(inputFileContent);
}
public override string GetDefaultExtension()
{
return ".Designer.cs";
}
}
}
[/csharp]
[text title=”<h4>Post buiild event of Repository.CodeGenerator</h4>”]
xcopy /y "$(ProjectDir)Repository.xsd" "$(DevEnvDir)….xmlschemas"
xcopy /y "$(ProjectDir)Repository.xsd" "$(SolutionDir)MyRuntime$(OutDir)"
call "$(DevEnvDir)….VCvcvarsall.bat"
regasm /codebase "$(TargetPath)"
regedit /s "$(ProjectDir)RepositoryCodeGenerator.reg"
[/text]
[text title=”<h4>Content of RepositoryCodeGenerator.reg</h4>”]
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftVisualStudio10.0Generators{00000000-0000-0000-0000-000000000000}RepositoryCodeGenerator2.0]
"GeneratesDesignTimeSource"=dword:00000001
"CLSID"="{11111111-1111-1111-1111-111111111111}"
<em id="__mceDel">
[/text]
[csharp title=”<h4>Content of MyGenerateRepositoryCode</h4>”]
public byte[] GenerateRepositoryCode(string inputFileContent, bool withAutoGenCode)
{
try
{
ValidateRepository(@"Repository.xsd", inputFileContent);
StringReader stringReader = new StringReader(inputFileContent);
StringWriter codeWriter = new StringWriter();
XDocument xDocument = XDocument.Load(stringReader, LoadOptions.SetLineInfo);
if (xDocument.Root != null)
{
CodeDomProvider codeDomProvider = new CSharpCodeProvider();
CodeNamespace buildNamespace = BuildNamespace(xDocument.Root);
CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions
{
BracingStyle = "C",
BlankLinesBetweenMembers = false
};
if (withAutoGenCode)
{
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(buildNamespace);
codeDomProvider.GenerateCodeFromCompileUnit(codeCompileUnit, codeWriter, codeGeneratorOptions);
}
else
{
codeDomProvider.GenerateCodeFromNamespace(buildNamespace, codeWriter, codeGeneratorOptions);
}
}
return Encoding.UTF8.GetBytes(codeWriter.ToString());
}
catch (Exception e)
{
// handle exception …
}
return null;
}
[/csharp]
[csharp title=”<h4>Example 'BuildNamespace function'</h4>”]
internal CodeNamespace BuildNamespace(XElement root)
{
XAttribute rootAttrib = root.Attribute("Name");
CodeNamespace ns = null;
if (rootAttrib != null && rootAttrib.Value != "")
{
XAttribute versionAttr = root.Attribute("Version");
if (versionAttr != null)
{
double ver;
if (double.TryParse(versionAttr.Value, out ver))
{
version = ver;
}
}
ns = new CodeNamespace(rootAttrib.Value);
foreach (Type type in basicTypes)
{
ns.Imports.Add(new CodeNamespaceImport(type.Namespace));
}
BuildClass(ns, null, root);
}
return ns;
}
[/csharp]
… and so on…
Remarks
- Replace ‘00000000-0000-0000-0000-000000000000’ with a newly generated GUID.
- replace ‘11111111-1111-1111-1111-111111111111′ with newly generated GUID.
Resources
Custom Tools Explained – CodeProject.
XSD -> Classes Generator Custom Tool (radically new and extensible) – Daniel Cazzulino’s Blog.
BaseCodeGenerator Class (Microsoft.VisualStudio.TextTemplating.VSHost).