You should be able to do the following:
0. Remove old (bad practice) COM code
- Edit your project build settings to not have "Register for COM interop" checked.
Edit your AssemblyInfo.cs and set
ComVisible
tofalse
:[assembly: ComVisible(false)]
Assuming your generator is named
MyCodeGenerator
, open the definition ofMyCodeGenerator
and add the attribute:[ComVisible(true)]
1. Edit your VSIX project to enable generation of a pkgdef file.
- Right click your project in Solution Explorer and select Unload Project.
- Right click the unloaded project and select Edit MyProject.csproj, where MyProject is the name of your project.
Locate the XML element
<GeneratePkgDefFile>
.- If the element exists, ensure that its value is set to
true
. Otherwise, add the following to the end of the first
<PropertyGroup>
element in your project file which does not have aCondition
attribute (this is almost always the firstPropertyGroup
in the file).<GeneratePkgDefFile>true</GeneratePkgDefFile>
- If the element exists, ensure that its value is set to
Repeat step 3 to set
<CopyBuildOutputToOutputDirectory>
totrue
.- Save and close the .csproj file.
- Right click the unloaded project in Solution Explorer and select Reload Project.
Open your project's source.extension.vsixmanifest file and locate the
<Content>
element. Add the following element as a child:<VsPackage>|%CurrentProject%|</VsPackage>
If your extension does not provide any other content elements, the entire
<Content>
element would now be this:<Content> <VsPackage>|%CurrentProject%|</VsPackage> </Content>
2. Define the required attribute types
At the end of this answer are sections for ProvideGeneratorAttribute.cs and ProvideAssemblyObjectAttribute.cs. Add these files to your project.
3. Register the code generator class
- Open your project's AssemblyInfo.cs.
Assuming your custom code generator class is named
MyCodeGenerator
, add the following attribute to the assembly info file.[assembly: ProvideAssemblyObject(typeof(MyCodeGenerator))]
4. Associate your code generator with the language service
- Open your project's AssemblyInfo.cs.
Assuming your custom code generator class is named
MyCodeGenerator
, and you want to register the code generator with the C# language service, add the following attribute to the assembly info file.[assembly: ProvideGenerator( typeof(MyCodeGenerator), VSConstants.UICONTEXT.CSharpProject_string, Description = "Description of the generator", GeneratesDesignTimeSource = true)]
Appendix A: ProvideGeneratorAttribute.cs
Disclaimer: This code is completely untested.
using System;
using Microsoft.VisualStudio.Shell;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ProvideGeneratorAttribute : RegistrationAttribute
{
private readonly Type _generatorType;
private readonly Guid _languageServiceGuid;
private string _name;
private string _description;
private bool _generatesDesignTimeSource;
public ProvideGeneratorAttribute(Type generatorType, string languageServiceGuid)
{
if (generatorType == null)
throw new ArgumentNullException("generatorType");
if (languageServiceGuid == null)
throw new ArgumentNullException("languageServiceGuid");
if (string.IsNullOrEmpty(languageServiceGuid))
throw new ArgumentException("languageServiceGuid cannot be empty");
_generatorType = generatorType;
_languageServiceGuid = new Guid(languageServiceGuid);
_name = _generatorType.Name;
}
public Type GeneratorType
{
get
{
return _generatorType;
}
}
public Guid LanguageServiceGuid
{
get
{
return _languageServiceGuid;
}
}
public string Name
{
get
{
return _name;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (string.IsNullOrEmpty(value))
throw new ArgumentException("value cannot be empty");
_name = value;
}
}
public string Description
{
get
{
return _description;
}
set
{
_description = value;
}
}
public bool GeneratesDesignTimeSource
{
get
{
return _generatesDesignTimeSource;
}
set
{
_generatesDesignTimeSource = value;
}
}
private string RegistrationKey
{
get
{
return string.Format(@"Generators\{0}\{1}", LanguageServiceGuid.ToString("B"), Name);
}
}
public override void Register(RegistrationContext context)
{
using (Key key = context.CreateKey(RegistrationKey))
{
if (!string.IsNullOrEmpty(Description))
key.SetValue(string.Empty, Description);
key.SetValue("CLSID", GeneratorType.GUID.ToString("B"));
key.SetValue("GeneratesDesignTimeSource", GeneratesDesignTimeSource ? 1 : 0);
}
}
public override void Unregister(RegistrationContext context)
{
context.RemoveKey(RegistrationKey);
}
}
Appendix B: ProvideAssemblyObjectAttribute.cs
Disclaimer: This code is completely untested.
using System;
using Microsoft.VisualStudio.Shell;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ProvideAssemblyObjectAttribute : RegistrationAttribute
{
private readonly Type _objectType;
private RegistrationMethod _registrationMethod;
public ProvideAssemblyObjectAttribute(Type objectType)
{
if (objectType == null)
throw new ArgumentNullException("objectType");
_objectType = objectType;
}
public Type ObjectType
{
get
{
return _objectType;
}
}
public RegistrationMethod RegistrationMethod
{
get
{
return _registrationMethod;
}
set
{
_registrationMethod = value;
}
}
private string ClsidRegKey
{
get
{
return string.Format(@"CLSID\{0}", ObjectType.GUID.ToString("B"));
}
}
public override void Register(RegistrationContext context)
{
using (Key key = context.CreateKey(ClsidRegKey))
{
key.SetValue(string.Empty, ObjectType.FullName);
key.SetValue("InprocServer32", context.InprocServerPath);
key.SetValue("Class", ObjectType.FullName);
if (context.RegistrationMethod != RegistrationMethod.Default)
_registrationMethod = context.RegistrationMethod;
switch (RegistrationMethod)
{
case Microsoft.VisualStudio.Shell.RegistrationMethod.Default:
case Microsoft.VisualStudio.Shell.RegistrationMethod.Assembly:
key.SetValue("Assembly", ObjectType.Assembly.FullName);
break;
case Microsoft.VisualStudio.Shell.RegistrationMethod.CodeBase:
key.SetValue("CodeBase", context.CodeBase);
break;
default:
throw new InvalidOperationException();
}
key.SetValue("ThreadingModel", "Both");
}
}
public override void Unregister(RegistrationContext context)
{
context.RemoveKey(ClsidRegKey);
}
}