I am often getting the questions about how to install the Custom Tool for Visual Studio let say you created for your colleagues in your team. Custom Tool – is a code generator installed to your IDE that generates code based on other file. You will see such generator for typed datasets, web services, etc.. Look for generator name specified in Custom Tool property in File Properties window in IDE for such files.
Actually there are really many ways to deploy your Custom Tool to the target machines. But usually the way I am describing below was good enough every time in my past development practice. So I did following to install my Custom Tools:
1. Run Visual Studio Command Prompt
2. Run the commands there (you could also create a batch file containing these commands) which are similar to these ones (VisualStudio.CodeGeneration.dll – assembly with my custom tools):
RegAsm.exe C:\..full..path.here..\VisualStudio.CodeGeneration.dll /unregister
GacUtil.exe /u VisualStudio.CodeGeneration
RegAsm.exe C:\..full..path.here..\VisualStudio.CodeGeneration.dll
GacUtil.exe /i C:\..full..path.here..\VisualStudio.CodeGeneration.dll
3. Sometimes Visual Studio requires to run ‘devenv /setup’ command to refresh IDE.
4. Enjoy your Custom Tool!
There are also some sample codelets to show what I usually had inside my Custom Tools:
[Guid("ХХХХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХХХХХХХХ")]
[ProgId("SqlServerQueryToCodeGenerator")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[CodeGeneratorName("SqlServerQueryToCodeGenerator")]
[CodeGeneratorLanguage("{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}",
"C# Generator for applying an XSLT to SQL XML query result")]
[CodeGeneratorLanguage("{E6FDF8B0-F3D1-11D4-8576-0002A516ECE8}",
"VB Generator for applying an XSLT to SQL XML query result")]
[CodeGeneratorLanguage("{164B10B9-B200-11D0-8C61-00A0C91E29D5}",
"J# Generator for applying an XSLT to SQL XML query result")]
[CodeGeneratorVSVersion(7, 0)]
[CodeGeneratorVSVersion(7, 1)]
public class SqlServerQueryToCodeGenerator : BaseCodeGeneratorWithSite {
[ComRegisterFunction]
public static void ComRegister(System.Type type) {
Registerer.Register(typeof(SqlServerQueryToCodeGenerator));
}
[ComUnregisterFunction]
public static void ComUnregister(System.Type type) {
Registerer.Unregister(typeof(SqlServerQueryToCodeGenerator));
}
public override string GetDefaultExtension() { return ".cs"; }
protected override byte[] GenerateCode(string file, string contents) { ... }
}
That is the Registerer class code:
using System;
using System.Diagnostics;
using Microsoft.Win32;
namespace VisualStudio.CodeGeneration {
public class Registerer {
public static void Register(Type type) {
foreach (CodeGeneratorLanguageAttribute languageAttribute in
CodeGeneratorLanguageAttribute.Get(type)) {
foreach (Version version in CodeGeneratorVSVersionAttribute.Get(type)) {
string codeGeneratorName = CodeGeneratorNameAttribute.Get(type);
Register(type, languageAttribute.Guid,
languageAttribute.Description, version, codeGeneratorName);
}
}
}
private static void Register(Type type, string languageGuid,
string description, Version vsVersion, string codeGeneratorName) {
Debug.Assert(languageGuid != null);
Debug.Assert(languageGuid.Length != 0);
Debug.Assert(description != null);
// Register only if base key is found.
string baseKeyPath = CombineKeyPath(GetGeneratorsRegKeyPath(vsVersion), languageGuid);
RegistryKey baseKey = Registry.LocalMachine.OpenSubKey(baseKeyPath, true);
if (baseKey == null)
return;
using (baseKey) {
using (RegistryKey toolKey = Registry.LocalMachine.CreateSubKey(
CombineKeyPath(baseKeyPath, codeGeneratorName))) {
toolKey.SetValue(string.Empty, description);
toolKey.SetValue("CLSID", type.GUID.ToString("B").ToUpper());
toolKey.SetValue("GeneratesDesignTimeSource", 1);
}
}
}
public static void Unregister(Type type) {
foreach (CodeGeneratorLanguageAttribute languageAttribute in
CodeGeneratorLanguageAttribute.Get(type)) {
foreach (Version version in CodeGeneratorVSVersionAttribute.Get(type)) {
string codeGeneratorName = CodeGeneratorNameAttribute.Get(type);
Unregister(languageAttribute.Guid, version, codeGeneratorName);
}
}
}
private static void Unregister(string languageGuid,
Version vsVersion, string codeGeneratorName) {
Debug.Assert(languageGuid != null);
Debug.Assert(languageGuid.Length != 0);
string keyPath = CombineKeyPath(GetGeneratorsRegKeyPath(vsVersion),
CombineKeyPath(languageGuid, codeGeneratorName));
Registry.LocalMachine.DeleteSubKey(keyPath, false);
}
private static string GetGeneratorsRegKeyPath(Version vsVersion) {
return string.Format(@"SOFTWARE\Microsoft\VisualStudio\{0}.{1}\Generators",
vsVersion.Major.ToString(), vsVersion.Minor.ToString());
}
private static string CombineKeyPath(string leftPart, string rightPart) {
Debug.Assert(leftPart != null);
Debug.Assert(leftPart.Length != 0);
Debug.Assert(rightPart != null);
Debug.Assert(rightPart.Length != 0);
if (leftPart[leftPart.Length - 1] == '\\')
return leftPart + rightPart;
return string.Concat(leftPart, @"\", rightPart);
}
}
}
You see there are used some custom attributes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class CodeGeneratorVSVersionAttribute : Attribute {
private Version version;
public CodeGeneratorVSVersionAttribute(int major, int minor) {
this.version = new Version(major, minor);
}
public static Version[] Get(Type type) {
object[] attributes = type.GetCustomAttributes(
typeof(CodeGeneratorVSVersionAttribute), false);
Debug.Assert(attributes.Length > 1);
ArrayList result = new ArrayList(attributes.Length);
foreach (CodeGeneratorVSVersionAttribute vsVersionAttribute in attributes) {
result.Add(vsVersionAttribute.version);
}
return (Version[])result.ToArray(typeof(Version));
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class CodeGeneratorLanguageAttribute : Attribute {
public readonly string Guid;
public readonly string Description;
public CodeGeneratorLanguageAttribute(string guid, string description) {
Guid = guid;
Description = description;
}
public static ICollection Get(Type type) {
object[] attributes = type.GetCustomAttributes(
typeof(CodeGeneratorLanguageAttribute), false);
Debug.Assert(attributes.Length > 1);
return attributes;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class CodeGeneratorNameAttribute : Attribute {
private string codeGeneratorName;
public CodeGeneratorNameAttribute(string codeGeneratorName) {
this.codeGeneratorName = codeGeneratorName;
}
public static string Get(Type type) {
object[] attributes = type.GetCustomAttributes(
typeof(CodeGeneratorNameAttribute), false);
Debug.Assert(attributes.Length == 1);
return ((CodeGeneratorNameAttribute)attributes[0]).codeGeneratorName;
}
}
So you could use something similar and then create batch file and/or use any of available installers to create setup.exe for your Custom Tool.
Hope that helps and have a nice VSX-tending!