سؤال

I have developed a custom code generator and deploy it via a VSIX, the problem is I should register assembly via regasm.exe after installing VSIX, but I have seen some projects such as DSLTool with custom code generation that registers automatically, any body knows how can I have automatically registration in my VSIX project?

هل كانت مفيدة؟

المحلول

You should be able to do the following:

0. Remove old (bad practice) COM code

  1. Edit your project build settings to not have "Register for COM interop" checked.
  2. Edit your AssemblyInfo.cs and set ComVisible to false:

    [assembly: ComVisible(false)]
    
  3. Assuming your generator is named MyCodeGenerator, open the definition of MyCodeGenerator and add the attribute:

    [ComVisible(true)]
    

1. Edit your VSIX project to enable generation of a pkgdef file.

  1. Right click your project in Solution Explorer and select Unload Project.
  2. Right click the unloaded project and select Edit MyProject.csproj, where MyProject is the name of your project.
  3. Locate the XML element <GeneratePkgDefFile>.

    1. If the element exists, ensure that its value is set to true.
    2. Otherwise, add the following to the end of the first <PropertyGroup> element in your project file which does not have a Condition attribute (this is almost always the first PropertyGroup in the file).

      <GeneratePkgDefFile>true</GeneratePkgDefFile>
      
  4. Repeat step 3 to set <CopyBuildOutputToOutputDirectory> to true.

  5. Save and close the .csproj file.
  6. Right click the unloaded project in Solution Explorer and select Reload Project.
  7. 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

  1. Open your project's AssemblyInfo.cs.
  2. 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

  1. Open your project's AssemblyInfo.cs.
  2. 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);
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top