custom tool registration
-
Thursday, May 18, 2006 2:10 PM
Hello,
it there any way how to register managed custom tool (in VsPackage) into VS ? I found ProvideGeneratorAttribute in msdn documentation, but it seems to be missing.
ProvideObjectAttibute do part of the job (regarding docs) but not the main part. I'd like some ProvideXXX attribute even for this.
Thanks in advance
Answers
-
Monday, May 22, 2006 7:33 AM
I found it:
[
Guid("77BAF5D3-55AA-4878-BB75-D42747562E7D")]
[ProvideObject(typeof(GDataSetGenerator456), RegisterUsing = RegistrationMethod.CodeBase)]
[CodeGeneratorRegistration(typeof(GDataSetGenerator456), "Gordic C# Code Generator for XSD (456)", "{fae04ec1-301f-11d3-bf4b-00c04f79efbc}", GeneratesDesignTimeSource = true)]
public class GDataSetGenerator456 : BaseCodeGeneratorWithSite
...Its strange, that CodeGeneratorRegistrationAttribute is not prefixed with "Provide" as many other regpkg attributes.
All Replies
-
Monday, May 22, 2006 7:33 AM
I found it:
[
Guid("77BAF5D3-55AA-4878-BB75-D42747562E7D")]
[ProvideObject(typeof(GDataSetGenerator456), RegisterUsing = RegistrationMethod.CodeBase)]
[CodeGeneratorRegistration(typeof(GDataSetGenerator456), "Gordic C# Code Generator for XSD (456)", "{fae04ec1-301f-11d3-bf4b-00c04f79efbc}", GeneratesDesignTimeSource = true)]
public class GDataSetGenerator456 : BaseCodeGeneratorWithSite
...Its strange, that CodeGeneratorRegistrationAttribute is not prefixed with "Provide" as many other regpkg attributes.
-
Monday, May 29, 2006 6:02 PMModerator
Hello, maliger!
I am not dure I understand what did you mean but if the question was: How to register custom tool and make it available for work? The answer is following way:
in example we have
- assembly with custum tool we want to register MyCustomTool.dll placed at root of drive C:/
- the MyNamespace.MyCustomTool is a namespace of the generator class(es)
to register custom tools in this assembly in your system:
- Run VS Command Line
- Type command: RegAsm.exe C:\MyCustomTool.dll
- Type command: GacUtil.exe /i C:\MyCustomTool.dll
- That is all! Custom tool registered. Sometimes you should relaunch the VS
to remove custom tool from your system:
- Run VS Command Line
- Type command: RegAsm.exe C:\MyCustomTool.dll /unregister
- Type command: GacUtil.exe /u MyNamespace.MyCustomTool
- That is all! Custom tool is removed from the system.
Hope it will help you. But maybe you meant something differ?
-
Wednesday, May 31, 2006 12:48 PM
Hello,
neither I'm sure, if we speak about the same thing. Too many things is names "custom tool". I mean tool, which you write to "Custom tool" field in File Properties. Like "MSDatasetGenerator".
As far as I know, this to work correctly, needs COM registration (regasm) + few more registry entries. See for example "Registering Custom Tools" in VSSDK. Or look into "Generators" registry entry under VS root. RegAsm know about those as well?
But mine original post was about regpkg tool, which is used with VsPackage. I coudn't find atribute for those Generator entries. Found them (2nd post) but, it seems to have a little inconsistent name (not ProvideXXX).
Anyway, thanks alot for response! Hope, this info'll help someone.
Regards,
-
Friday, February 27, 2009 6:10 PMI've heard this so many times, and frankly, you are all wrong:
[CodeGeneratorRegistration(typeof(GDataSetGenerator456), "Gordic C# Code Generator for XSD (456)", "{fae04ec1-301f-11d3-bf4b-00c04f79efbc}", GeneratesDesignTimeSource = true)]
This attribute does not add the appropriate Registry Entries under HKLM\Software\Microsoft\VisualStudio\Generators as it is supposed to do. I've used the one in the 2008 SDK as well as re-writing the class myself, but it doesn't work. The only way to achieve the appropriate end here, is to write attributed methods for ComRegisterFunction and ComUnregisterFunction.
Secondly, even after you follow these supposedly "simple" steps, or regasm and gacutil they generator doesn't work.
I've scanned the entire registry for SingleFileSettingsGenerator and found that it exists under the VisualStudio\Generators\{VBGuid|VCGuid}\SingleFileSettingsGenerator, however, the CLSID associated with this Code Generator does not exist in the global CLSID cache. (HKCR\CLSDI). It only exists in
{HKLM|HKCU}\Sorftware\VisualStudio\CLSID
As well, it may exist under VSTA, VBExpress, or VCSExpress etc dependent upon what else you have installed. So frankly I love trying to research this because my code is HALTED because I can't create a simple generator to take my better code ideas for the settings management part of the standard application.
I've posted other requests about this but nobody has an answer, and all i find out here is : Oh just this simple two step process. Simple my foot, because frankly they don't work!
Jaeden "Sifo Dyas" al'Raec Ruiner
-
Thursday, February 11, 2010 2:42 PMHey, Did you find any solution to this?
-
Saturday, February 13, 2010 2:49 AMYes.
As soon as I fix the problem of installing SQL Server 2008 on my home system I will come back to address this. Basically you have to write the attribute yourself to handle the intricacies of COM Component installation into the VS extensibility. I have one I developed and I will be releasing massive amounts of code in the next month which not only handles this but may other .net framework oversights.
J"SD"a'RR
"Never Trust a computer. Your brain is smarter than any micro-chip."
PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer. -
Thursday, April 08, 2010 5:14 AMJaedenRuiner , I am interested in registering the com myself, as you are correct the attribute alone does not work. I'll probably work this out some myself, but it's been years since I've delved into COM, and it would be nice to see the mistakes I make :P. I've also seen your name in other posts regarding generators, and I won't be able to use my generator until it can self-register itself as my customer won't run external tools/.reg files.
-
Thursday, April 08, 2010 6:25 AM
TamusJRoyce: (bear with me. The editor here at MSDN sucks so bad, i've had to delete twice, and then reformat using the HTML direct source. I wish they would take a lesson from the BBCode Forums out there. I never have problems with the php based forums like those - kinda says something doesn't it.)
Well I dove into my source, and for Tool Registration, I do recommend following all the particular step-by-step stuff you can find out there, as I did, i just tweaked some things for my purposes to handle things a bit more my way, as i'm a kind-a do-it-right-the-first-time sort of developer, which means a lot of .Net code and ideas I have re-written because they didn't follow that strategy.
the Manner in which I register my custom component is two fold. First I have the Attribute which if you truly delve into the intricacies of tool generators, you will have to find the other lines for yourself regarding languages other than C# and VB, and you apply that attribute to the SiteCOdeGenerator class you've descended from.
Second, I was just very lazy and made a command line batch file to handle the regasm commands for me, and simply put them in pre-post build events to remove the old version and install the new one. So All i do to register my customer generate is compile my project. heheheh.
I have included the code below for yours and others future use. Now, I originally wrote all this for when I was still using VB Express before my upgrade to VS Pro (both 2008 editions) So I do believe it will still work for both, even though originally the express versions weren't suppose to let you do this, I cheated. I have no idea how to set it up for 2010, it may be the same, it may not, as well I did not do any look ups for other languages, J#, xml, etc.
GeneratorRegistrationAttribute:
/*========================================================================= * File: GeneratorRegistrationAttribute.cs * Author: Sean Kendrick * Creation Date: Today * Copyright: © 2009 Quikwork Systems, LLC * Purpose: SiteCodeGenerator Attribute for Registering a Custom Tool * Generator COM Component *=========================================================================*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using VSLangProj80; using Microsoft.Win32; using Microsoft.VisualStudio.Shell; using FlufExtensions; /// <summary> /// Custom Tool Registration Attribute Handles the Registration of a Custom Tool Class /// <para>Derived from the <see cref="Microsoft.VisualStudio.Shell.RegistrationAttribute"/> Abstract class</para> /// </summary> /// <remarks>The Overridden Constructors provide differing access to the default /// values of properties for use during Registration, primarily <see cref="GeneratorRegistrationAttribute.CreateSource"/> /// defaults to true and <see cref="GeneratorRegistrationAttribute.CreateSharedSource"/>defaults to false.</remarks> [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public sealed class GeneratorRegistrationAttribute : RegistrationAttribute { #region -------------::< Fields & Constants >::------------- private const string KEYFMT = @"Generators\{0}\{1}"; private const string CLSID = @"CLSID"; private const string GENSRC = @"GeneratesDesignTimeSource"; private const string GENSHR = @"GeneratesSharedDesignTimeSource"; internal const string PROCSVR = @"InprocServer32"; private const string MSCORE = @"C:\WINDOWS\system32\mscoree.dll"; private const string ASSMBL = @"Assembly"; private const string MODEL = @"ThreadingModel"; private const string CLASS = @"Class"; internal const string CODEBASE = @"Codebase"; private Type _toolType; private string _toolDesc; private string _language; private bool _source; private bool _shared; #endregion #region -------------::< Class Properties >::------------- /// <summary> /// Accessor for the Custom Tool Class's Type /// </summary> /// <value>System.Type</value> public Type ToolType { get { return _toolType; } } /// <summary> /// Accessor for the Custom Tool's Description /// </summary> /// <value>String</value> public string Description { get { return _toolDesc; } } /// <summary> /// Accessor for the Language Context GUID, used to determine Generator's /// Language Sub key during registration into the specific Visual Studio IDE's. /// </summary> public string Language { get { return _language; } } /// <summary> /// Accessor for the GenerateDesignTimeSource Registry Flag /// Indicating this Custom Tool Generates a Design Time Source File. /// </summary> public bool CreateSource { get { return _source; } set { _source = value; } } /// <summary> /// Accessor for the GenerateSharedDesignTimeSource Registry Flag /// Indicating this Custom Tool Generates a Shared Design Time Source File /// </summary> public bool CreateSharedSource { get { return _shared; } set { _shared = value; } } #endregion #region -------------::< Class Constructors >::------------- /// <summary> /// Overloaded Constructor /// <para>Provides the values for <paramref name="generatorType"/>, <paramref name="generatorDesc"/>, /// and <paramref name="contextGuid"/> for Registering the Custom Tool, Assuming default values for /// <see cref="CreateSource"/> and <see cref="CreateSharedSource"/>.</para> /// </summary> /// <param name="generatorType">The System.Type describing the Custom Tool Class (<see cref="typeof"/>).</param> /// <param name="generatorDesc">Description Element of the Custom Tool</param> /// <param name="Context">Language Specific Context GUID (<see cref="VSLangProj80.vsContextGuids"/>).</param> public GeneratorRegistrationAttribute(Type generatorType, string generatorDesc, string contextGuid) : this(generatorType, generatorDesc, contextGuid, true, false) { } /// <summary> /// Overloaded Constructor /// <para>Provides the values for <paramref name="generatorType"/>, <paramref name="generatorDesc"/>, /// <paramref name="contextGuid"/>, and <paramref name="createSource"/> for Registering the Custom Tool, Assuming /// the default value for <see cref="CreateSharedSource"/>.</para> /// </summary> /// <param name="generatorType">The System.Type describing the Custom Tool Class (<see cref="typeof"/>).</param> /// <param name="generatorDesc">Description Element of the Custom Tool</param> /// <param name="contextGuid">Language Specific Context GUID (<see cref="VSLangProj80.vsContextGuids"/>)</param> /// <param name="createSource">Boolean Flag Setting the <see cref="CreateSource"/> property</param> public GeneratorRegistrationAttribute(Type generatorType, string generatorDesc, string contextGuid, bool createSource) : this(generatorType, generatorDesc, contextGuid, createSource, false) { } /// <summary> /// Overloaded Constructor /// <para>Provides the values for <paramref name="generatorType"/>, <paramref name="generatorDesc"/>, /// <paramref name="Context"/>, <paramref name="createSource"/>, and <paramref name="createShared"/> /// for Registering the Custom Tool.</para> /// </summary> /// <param name="generatorType">The System.Type describing the Custom Tool Class (<see cref="typeof"/>).</param> /// <param name="generatorDesc">Description Element of the Custom Tool</param> /// <param name="contextGuid">Language Specific Context GUID (<see cref="VSLangProj80.vsContextGuids"/>).</param> /// <param name="createSource">Boolean Flag Setting the <see cref="CreateSource"/> property</param> /// <param name="createShared">Boolean Flag Setting the <see cref="CreateSharedSource"/> property</param> public GeneratorRegistrationAttribute(Type generatorType, string generatorDesc, string contextGuid, bool createSource, bool createShared) { if (generatorType == null) throw new ArgumentNullException("generatorType"); if (generatorDesc == null) throw new ArgumentNullException("generatorDesc"); if (contextGuid == null) throw new ArgumentNullException("contextGuid"); _toolDesc = generatorDesc; _toolType = generatorType; _language = contextGuid; this.CreateSource = createSource; this.CreateSharedSource = createShared; } #endregion #region -------------::< Public Methods >::------------- /// <summary> /// Overridden Abstract MethDecl handling the registration of the Custom Tool into /// the System Registry based on provided properties. /// </summary> /// <param name="context">Provided RegistrationContext for the base Registry key /// from which to add further Registration subkeys values.</param> public override void Register(RegistrationContext context) { using (Key generatorKey = context.CreateKey(KEYFMT.FmtStr(_language, _toolType.Name))) { generatorKey.SetValue(string.Empty, _toolDesc); generatorKey.SetValue(CLSID, _toolType.GUID.ToString("B")); generatorKey.SetValue(GENSRC, _source ? 1 : 0); if (_shared) generatorKey.SetValue(GENSHR, 1); } using (Key key = context.CreateKey(CLSID + @"\" + _toolType.GUID.ToString("B"))) { key.SetValue(string.Empty, _toolDesc); //Console.WriteLine("ProcSvr: " + MSCORE); key.SetValue(PROCSVR, MSCORE); key.SetValue(MODEL, "Both"); //Console.WriteLine("Class:" + _toolType.FullName); key.SetValue(CLASS, _toolType.FullName); //Console.WriteLine("Assembly: " + _toolType.AssemblyQualifiedName); key.SetValue(ASSMBL, _toolType.AssemblyQualifiedName); } } /// <summary> /// Overridden Abstract MethDecl handling the removal of the Custom Tool from /// the System Registry based on provided properties. /// </summary> /// <param name="context">Provided RegistrationContext for the base Registry key /// from which to remove previous registered subkeys and values.</param> public override void Unregister(RegistrationContext context) { context.RemoveKey(KEYFMT.FmtStr(_language, _toolType.Name)); context.RemoveKey(CLSID + @"\" + _toolType.GUID.ToString("B")); } public void Register(RegistryKey baseKey, string CodeFile) { if (baseKey.isValid()) { string tmpKey = string.Empty; if (!baseKey.OpenSubKey(tmpKey = KEYFMT.FmtStr(_language, _toolType.Name)).isValid()) baseKey.CreateSubKey(tmpKey); //Console.WriteLine(tmpKey); using (RegistryKey key = baseKey.OpenSubKey(tmpKey, true)) { //Console.WriteLine("Desc: " + _toolDesc); key.SetValue(string.Empty, _toolDesc); //Console.WriteLine("Guid: " + _toolType.GUID.ToString("B")); key.SetValue(CLSID, _toolType.GUID.ToString("B")); //Console.WriteLine("GenSrc: " + (_source ? 1 : 0)); key.SetValue(GENSRC, _source ? 1 : 0); if (_shared) key.SetValue(GENSHR, 1); } if (!baseKey.OpenSubKey(tmpKey = CLSID + @"\" + _toolType.GUID.ToString("B")).isValid()) baseKey.CreateSubKey(tmpKey); //Console.WriteLine(tmpKey); using (RegistryKey key = baseKey.OpenSubKey(tmpKey, true)) { key.SetValue(string.Empty, _toolDesc); //Console.WriteLine("ProcSvr: " + MSCORE); key.SetValue(PROCSVR, MSCORE); key.SetValue(MODEL, "Both"); //Console.WriteLine("Class:" + _toolType.FullName); key.SetValue(CLASS, _toolType.FullName); //Console.WriteLine("Assembly: " + _toolType.AssemblyQualifiedName); key.SetValue(ASSMBL, _toolType.AssemblyQualifiedName); if (CodeFile.isValid()) key.SetValue(CODEBASE, CodeFile); } } } public void Unregister(RegistryKey baseKey) { try { string tmpKey = string.Empty; if (baseKey.OpenSubKey(tmpKey = KEYFMT.FmtStr(_language, _toolType.Name), true).isValid()) baseKey.DeleteSubKey(tmpKey); if (baseKey.OpenSubKey(tmpKey = CLSID + @"\" + _toolType.GUID.ToString("B"), true).isValid()) baseKey.DeleteSubKey(tmpKey); } catch (Exception) { } } #endregion }
SiteCodeGenerator Class Methods:
/// <summary> /// TODO: Documentation /// </summary> /// <remarks>[DefaultRegistryRoot(@"Software\Microsoft\VisualStudio\9.0")]</remarks> [ComVisible(true)] [Guid("EB8B910F-CE35-46d6-B62C-930AB6C910FB")] [GeneratorRegistration(typeof(FlufCodeFileGenerator), @"QuikWorks VB Custom File Generators", vsContextGuids.vsContextGuidVBProject, true)] //, false, true)] [GeneratorRegistration(typeof(FlufCodeFileGenerator), @"QuikWorks VC# Custom File Generator", vsContextGuids.vsContextGuidVCSProject, true)] //, false, true)] [ProvideObject(typeof(FlufCodeFileGenerator))] public class FlufCodeFileGenerator : SiteCodeGenerator { #region -------------::< Public Methods >::------------- /// <summary> /// TODO: Documentation /// </summary> /// <param name="contextType"></param> [ComRegisterFunction] public static void Register(string contextType) { string subKey = string.Empty; RegistryKey rootKey = contextType.OpenBaseRegKey(ref subKey); Attribute[] attrs = (Attribute[])typeof(FlufCodeFileGenerator).GetCustomAttributes(typeof(GeneratorRegistrationAttribute), false); RegistryKey baseKey = null; string codeFile = null; if (rootKey.isValid()) { codeFile = (string)rootKey.OpenSubKey(subKey + '\\' + GeneratorRegistrationAttribute.PROCSVR).GetValue(GeneratorRegistrationAttribute.CODEBASE); codeFile = codeFile.Replace("file:///", "").Replace('/', '\\'); Console.WriteLine(codeFile); } rootKey.DeleteSubKeyTree(subKey); foreach (GeneratorRegistrationAttribute attr in attrs) { foreach (string vs in Properties.Resources.REG_VSKEY.Split(',')) { baseKey = Registry.LocalMachine.OpenSubKey(Properties.Resources.REG_BASEKEY.FmtStr(vs, vsVer.ToString()), true); if (baseKey.isValid()) attr.Register(baseKey, codeFile); } } } /// <summary> /// TODO: Documentation /// </summary> /// <param name="contextType"></param> [ComUnregisterFunction] public static void Unregister(Type contextType) { Attribute[] attrs = (Attribute[])typeof(FlufCodeFileGenerator).GetCustomAttributes(typeof(GeneratorRegistrationAttribute), false); RegistryKey baseKey = null; foreach (GeneratorRegistrationAttribute attr in attrs) { foreach (string vs in Properties.Resources.REG_VSKEY.Split(',')) { baseKey = Registry.LocalMachine.OpenSubKey(Properties.Resources.REG_BASEKEY.FmtStr(vs, vsVer.ToString()), true); if (baseKey.isValid()) attr.Unregister(baseKey); } } } #endregion }
Project Resources:
/* Resources.Designer.cs */ /// <summary> /// Looks up a localized string similar to Software\Microsoft\{0}\{1}. /// </summary> internal static string REG_BASEKEY { get { return ResourceManager.GetString("REG_BASEKEY", resourceCulture); } } /// <summary> /// Looks up a localized string similar to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion|RegisteredOrganization. /// </summary> internal static string REG_ORGKEY { get { return ResourceManager.GetString("REG_ORGKEY", resourceCulture); } } /// <summary> /// Looks up a localized string similar to VisualStudio\,VCSExpress\,VBExpress\. /// </summary> internal static string REG_VSKEY { get { return ResourceManager.GetString("REG_VSKEY", resourceCulture); } } /* Resource Values from the resx XML file */ <data name="REG_BASEKEY" xml:space="preserve"> <value>Software\Microsoft\{0}\{1}</value> <comment>RegKeys</comment> </data> <data name="REG_ORGKEY" xml:space="preserve"> <value>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion|RegisteredOrganization</value> <comment>RegKeys</comment> </data> <data name="REG_VSKEY" xml:space="preserve"> <value>VisualStudio\,VCSExpress\,VBExpress\</value> <comment>RegKeys</comment> </data>
Extensions Under using FlufExtensions:
/// <summary> /// Wrapper for the string.Format() Shared Function. /// </summary> /// <param name="Item">Calling string Object</param> /// <param name="args">ParamArray of Format Argumnets</param> /// <returns>string</returns> /// <remarks></remarks> public static string FmtStr(this string item, params object[] args) { return item.isValid() ? string.Format(CultureInfo.InvariantCulture, item, args) : string.Empty; } /// <summary> /// Cross-Board object comparison. Similar to Assert but without any negative effects, simple bool return /// </summary> /// <param name="items">object Calling Extension</param> /// <returns>bool - Is a Valid object or Not</returns> /// <remarks></remarks> public static bool isValid<T>(this T self) { return (self != null); } /// <summary> /// returns if a string is Valid (! Blank) /// </summary> /// <param name="item">Calling string Object</param> /// <returns>bool</returns> /// <remarks></remarks> public static bool isValid(this string item) { return (item != null) && (item.Length > 0); }
A Little note, my Batch command structure uses all the Extensions and such for batch programming so you may need to modify and/or update your system to work with these.
Batch Commands:
/ * Pre-Build Event command line: */ c:\Batch\reg.cmd "$(TargetPath)" /u /* Post-Build Event Command line: */ c:\Batch\reg.cmd "$(TargetPath)" / * c:\Batch\reg.cmd */ @ECHO OFF & SETLOCAL SET MODE=%2 SET MODE=%MODE:~-1% SET FILE=%1 SET FILE=%FILE:~1,-1% REM ECHO Mode=%MODE% REM ECHO File=%FILE% IF NOT "%FILE%"=="" ( IF EXIST "%FILE%" ( IF /I %MODE%==U ( CALL :unreg ) ELSE ( CALL :register ) GOTO :END ) ELSE ( ECHO Assembly File: "%FILE%" Does not Exist! ) ) GOTO :END :unreg ECHO UnRegistering Assembly: %FILE% C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /u "%FILE%" >nul GOTO :eof :register ECHO Registering Assembly: %FILE% C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /codebase "%FILE%" >nul GOTO :eof :END ENDLOCAL & EXIT /B 0
You may notice the exit /b 0 at the end. this was put in there to handle when an error occurred during the reg/unreg process that if the regasm (and gacutil for my other batch file for different projects) cause in error the c#/vb compiler bail on the build and claim it wasn't successful. For the post-build event that's find but for the pre-build, if the assembly isn't registered or not found by the file it can prevent the whole project from building, so...ya...know, it takes an effort but we eventually make these products work for us. I hope this helps anyone in the future, because oddly enough, once i write something that works, i tend not to go back over it, so sometimes I have to relearn what i've done as if someone else wrote it. *cheezygrin*
Cheers!Jaeden "Sifo Dyas" al'Raec Ruiner
"Never Trust a computer. Your brain is smarter than any micro-chip."
PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.- Proposed As Answer by JaedenRuiner Thursday, April 08, 2010 6:25 AM

