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)]publicclass SqlServerQueryToCodeGenerator : BaseCodeGeneratorWithSite { [ComRegisterFunction]publicstaticvoid ComRegister(System.Type type) { Registerer.Register(typeof(SqlServerQueryToCodeGenerator)); } [ComUnregisterFunction] publicstaticvoid ComUnregister(System.Type type) { Registerer.Unregister(typeof(SqlServerQueryToCodeGenerator)); }publicoverridestring GetDefaultExtension() { return".cs"; }protectedoverridebyte[] GenerateCode(string file, string contents) { ... } }
That is the Registerer class code:
using System;using System.Diagnostics;using Microsoft.Win32;namespace VisualStudio.CodeGeneration {publicclass Registerer {publicstaticvoid 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); } } }privatestaticvoid 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); } } }publicstaticvoid 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); } } }privatestaticvoid 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); }privatestaticstring GetGeneratorsRegKeyPath(Version vsVersion) {returnstring.Format(@"SOFTWARE\Microsoft\VisualStudio\{0}.{1}\Generators", vsVersion.Major.ToString(), vsVersion.Minor.ToString()); }privatestaticstring 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;returnstring.Concat(leftPart, @"\", rightPart); } } }
You see there are used some custom attributes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]publicclass CodeGeneratorVSVersionAttribute : Attribute {private Version version;public CodeGeneratorVSVersionAttribute(int major, int minor) {this.version = new Version(major, minor); }publicstatic 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)]publicclass CodeGeneratorLanguageAttribute : Attribute {publicreadonlystring Guid;publicreadonlystring Description;public CodeGeneratorLanguageAttribute(string guid, string description) { Guid = guid; Description = description; }publicstatic ICollection Get(Type type) {object[] attributes = type.GetCustomAttributes(
typeof(CodeGeneratorLanguageAttribute), false); Debug.Assert(attributes.Length > 1);return attributes; } } [AttributeUsage(AttributeTargets.Class)] publicclass CodeGeneratorNameAttribute : Attribute {privatestring codeGeneratorName;public CodeGeneratorNameAttribute(string codeGeneratorName) {this.codeGeneratorName = codeGeneratorName; }publicstaticstring 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!