如何使用 Visual Studio 2017 或 Visual Studio 2019 注册 "custom tool" 以使其生效?

How to register "custom tool" with Visual Studio 2017 or Visual Studio 2019 to make it work?

背景: 我们有一个带有 Visual studio 的 custom tool which takes the xml input and generates the cs output. A custom tool needs to register 以使其与那个版本的 visual studio 一起工作。

我们所做的: 我们已经用 Visual Studio 2015 完成了 custom tool registration,工作正常。但现在问题出在 Visual Studio 2017.

问题: 到目前为止,在我的研究中,我发现直到 Visual Studio 2015 年,VS 都有允许注册该工具的直接注册表项,但从 VS 2017 开始, Microsoft 更改了注册表项的存储方式 (a good read to understand changes in VS2017)。

如果我打开 VS 2017 并尝试 运行 自定义工具然后我得到错误

Cannot find custom tool "Tool Name" on this system.

这很明显,因为自定义工具尚未在 VS 2017 中注册才能工作。

我试着按照 this guy 说要将 .bin 文件加载到注册表中,但他还说它无法启动 VS 2017。为了启动 VS,我们必须卸载配置单元。研究表明,.bin 文件可以根据安装的 VS 类型(企业版、专业版等)位于不同的位置。

以前有人做过吗?

TIA

嗯,在研究过程中,我得到了这个问题的答案。

解法:

  1. We have to load the the .bin file (via load hiv).
  2. Make the changes or edit the bin to register your tool.
  3. Unload hive.

步骤#1:加载 Hive。

a) Open registry (regedit). Select node HKEY_LOCAL_MACHINE.

b) Go to | File -> Load Hive

c) Select the bin file which is available at -> %LocalAppData%\ Microsoft\ VisualStudio\ 15.0_'instance_id'\privateregistry.bin.

d) Provide a key name. This will create a new key entry in HKEY_LOCAL_MACHINE with your key name.

e) You can check the .bin file at HKEY_LOCAL_MACHINE\YourKeyName\Software\Microsoft\VisualStudio\

步骤#2:编辑 bin:您现在可以按照与其他 VS 版本相同的方式注册您的自定义工具。实际上,唯一的问题是将 VS2017 密钥获取到全局注册表中,并使用上面的步骤#1 解决了这个问题。

步骤#3:卸载 Hive。

a) select your key under HKEY_LOCAL_MACHINE.

b) Go to | File menu

c) Unload Hive.

您可能需要通过创建一个 Visual Studio 扩展程序 (VSIX) 来遵循不同的方法,下面我已经详细解释了它,希望对您有所帮助。

如何在 Visual Studio 2017 中创建自定义工具或单个文件生成器:

在 VS2017 之前,创建自定义工具需要实现接口 IVsSingleFileGenerator 以及在系统注册表中注册和取消注册自定义工具的代码,但在 VS2017 中,Microsoft 已经更改了整个注册表结构。变化是,VS 会将注册表项添加到私有注册表中,这样系统注册表就不会乱七八糟。虽然以前注册表项是在系统注册表中创建的,但现在它们是

C:\Users\xyz\AppData\Local\Microsoft\VisualStudio.0_xx\privateregistry.bin

Visual studio 2017 还支持通过 运行 从 visual studio 本身 (F5) 直接测试您的工具,这会启动另一个名为 Visual Studio 的实例65=]Visual Studio 实验实例 并且您的工具可以在其中进行测试,因为它会在

中创建注册表项

C:\Users\xyz\AppData\Local\Microsoft\VisualStudio.0_xxExp\privateregistry.bin

按照以下步骤在 VS2017 中创建自定义工具:

  1. 我们需要创建一个 VSIX 扩展
  2. 添加新的 Visual Studio 包
  3. 实施IVsSingleFileGenerator
  4. 添加注册表项代码
  5. 通过运行在 VS2017 中编译和测试该工具
  6. 双击生成的 .VSIX 文件安装该工具

我们将创建一个名为 "CountLines" 的 extension/custom 工具作为示例,它将读取一个文件(将自定义工具 属性 设置为 CountLines)并生成一个 XML包含文件中行数的文件。例如<LineCount>1050</LineCount>

1.创建 VSIX 扩展 要创建扩展,您必须安装 Visual Studio 扩展工具,它作为可选功能包含在 Visual Studio 安装程序中。如果未安装,您也可以通过修改 VS 2017 安装程序来安装它。 通过 selecting

创建新的 VSIX(Visual Studio 扩展)项目

New Project -> Extensibility -> VSIX Project

给它起个像 "CountLinesVSIX" 这样的名字。

2。添加新的 Visual Studio 包 创建 VSIX 项目后,通过 selecting

添加新的 Visual Studio 包

Add -> New Item -> Extensibility -> Visual Studio Package

给它起个名字"CountLines.cs"。在 CountLines.cs 中,我们需要删除现有代码并将其替换为我们的 IVsSingleFileGenerator 实现代码

3。实施 IVsSingleFileGenerator 为接口 IVsSingleFileGenerator 编写您的自定义实现,我们的示例代码如下

using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System.Text;

namespace CountLinesVSIX
    {
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration( "CountLines", "Generate XML with line count", "1.0")] 
    [Guid("202E7E8B-557E-46CB-8A1D-3024AD68F44A")]
    [ComVisible(true)]
    [ProvideObject(typeof(CountLines))]
    [CodeGeneratorRegistration(typeof(CountLines), "CountLines", "{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}", GeneratesDesignTimeSource = true)]
    public sealed class CountLines : IVsSingleFileGenerator
    {

        #region IVsSingleFileGenerator Members

        public int DefaultExtension(out string pbstrDefaultExtension)
        {
            pbstrDefaultExtension = ".xml";
            return pbstrDefaultExtension.Length;
        }

        public int Generate(string wszInputFilePath, string bstrInputFileContents,
          string wszDefaultNamespace, IntPtr[] rgbOutputFileContents,
          out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
        {
            try
            {
                int lineCount = bstrInputFileContents.Split('\n').Length;
                byte[] bytes = Encoding.UTF8.GetBytes("<LineCount>" + lineCount.ToString() + "</LineCount>" );
                int length = bytes.Length;
                rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(length);
                Marshal.Copy(bytes, 0, rgbOutputFileContents[0], length);
                pcbOutput = (uint)length;
            }
            catch (Exception ex)
            {
                pcbOutput = 0;
            }
            return VSConstants.S_OK;
        }

        #endregion
    }
}

我们需要为我们的扩展程序提供一个唯一的 GUID,例如上面的代码 [Guid("202E7E8B-557E-46CB-8A1D-3024AD68F44A")]。可以通过 selecting "Tools -> Create GUID" 从 VS2017 创建 GUID。 Select GUID 格式作为注册表格式。请注意,上面代码中提到的 GUID 没有花括号。

COM 互操作需要

[ComVisible(true)]

[CodeGeneratorRegistration(typeof(CountLines), "CountLines", "{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}", GeneratesDesignTimeSource = true)] 是一个 class 属性,带有用于注册该工具的代码。参数为 GeneratorType、GeneratorName 和 C# 语言 GUID

您还可以从支持自定义 TextTemplate 格式的 "TemplatedCodeGenerator" 派生,这可能需要一些额外的代码实现。

4。添加注册表项代码 使用以下代码创建新的 class 文件,将其命名为 CodeGeneratorRegistrationAttribute.cs

using System;
using System.Globalization;
using Microsoft.VisualStudio.Shell;

namespace CountLinesVSIX
{

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class CodeGeneratorRegistrationAttribute : RegistrationAttribute
    {
        private string _contextGuid;
        private Type _generatorType;
        private Guid _generatorGuid;
        private string _generatorName;
        private string _generatorRegKeyName;
        private bool _generatesDesignTimeSource = false;
        private bool _generatesSharedDesignTimeSource = false;

        public CodeGeneratorRegistrationAttribute(Type generatorType, string generatorName, string contextGuid)
        {
            if (generatorType == null)
                throw new ArgumentNullException("generatorType");
            if (generatorName == null)
                throw new ArgumentNullException("generatorName");
            if (contextGuid == null)
                throw new ArgumentNullException("contextGuid");

            _contextGuid = contextGuid;
            _generatorType = generatorType;
            _generatorName = generatorName;
            _generatorRegKeyName = generatorType.Name;
            _generatorGuid = generatorType.GUID;
        }

        /// <summary> 
        /// Get the generator Type 
        /// </summary> 
        public Type GeneratorType
        {
            get { return _generatorType; }
        }

        /// <summary> 
        /// Get the Guid representing the project type 
        /// </summary> 
        public string ContextGuid
        {
            get { return _contextGuid; }
        }

        /// <summary> 
        /// Get the Guid representing the generator type 
        /// </summary> 
        public Guid GeneratorGuid
        {
            get { return _generatorGuid; }
        }

        /// <summary> 
        /// Get or Set the GeneratesDesignTimeSource value 
        /// </summary> 
        public bool GeneratesDesignTimeSource
        {
            get { return _generatesDesignTimeSource; }
            set { _generatesDesignTimeSource = value; }
        }

        /// <summary> 
        /// Get or Set the GeneratesSharedDesignTimeSource value 
        /// </summary> 
        public bool GeneratesSharedDesignTimeSource
        {
            get { return _generatesSharedDesignTimeSource; }
            set { _generatesSharedDesignTimeSource = value; }
        }


        /// <summary> 
        /// Gets the Generator name  
        /// </summary> 
        public string GeneratorName
        {
            get { return _generatorName; }
        }

        /// <summary> 
        /// Gets the Generator reg key name under  
        /// </summary> 
        public string GeneratorRegKeyName
        {
            get { return _generatorRegKeyName; }
            set { _generatorRegKeyName = value; }
        }

        /// <summary> 
        /// Property that gets the generator base key name 
        /// </summary> 
        private string GeneratorRegKey
        {
            get { return string.Format(CultureInfo.InvariantCulture, @"Generators\{0}\{1}", ContextGuid, GeneratorRegKeyName); }
        }
        /// <summary> 
        ///     Called to register this attribute with the given context.  The context 
        ///     contains the location where the registration inforomation should be placed. 
        ///     It also contains other information such as the type being registered and path information. 
        /// </summary> 
        public override void Register(RegistrationContext context)
        {
            using (Key childKey = context.CreateKey(GeneratorRegKey))
            {
                childKey.SetValue(string.Empty, GeneratorName);
                childKey.SetValue("CLSID", GeneratorGuid.ToString("B"));

                if (GeneratesDesignTimeSource)
                    childKey.SetValue("GeneratesDesignTimeSource", 1);

                if (GeneratesSharedDesignTimeSource)
                    childKey.SetValue("GeneratesSharedDesignTimeSource", 1);

            }
        }

        /// <summary> 
        /// Unregister this file extension. 
        /// </summary> 
        /// <param name="context"></param> 
        public override void Unregister(RegistrationContext context)
        {
            context.RemoveKey(GeneratorRegKey);
        }
    }
}

以上代码将确保您的条目已输入到 VS 私有注册表

5.通过 运行在 VS2017 中编译和测试该工具 您可以在 "source.extension.vsixmanifest" 中添加 "Install targets" 以确保您的扩展支持不同的 VS2017 版本。 运行 你在 VS 2017 中的工具来测试它是否按预期工作。一旦你 运行 VSIX,Visual Studio 实验实例将安装扩展并在注册表 "C:\Users\xyz\AppData\Local\Microsoft\VisualStudio.0_xxExp\privateregistry.bin" 中注册它。您可以通过 selecting "Tools -> Extensions and updates" 查看已安装的扩展。要测试该工具,我们必须打开一个虚拟项目,select 解决方案资源管理器中的一个文件,转到其属性并将自定义工具 属性 更新为 "CountLines"。完成此操作后,VS 将在后台 运行 工具并生成输出,在我们的示例中,它将在 selected 文件下生成一个 xml 文件。或者,设置自定义工具 属性 后,您可以右键单击该文件并 select "Run Custom Tool"

6.双击生成的 .VSIX 文件安装该工具 测试成功后,尝试安装位于 "projectName/bin/debug" 位置的 VSIX。双击文件安装 VSIX,按照安装步骤操作。现在你的工具可以在 VS2017 中使用了。使用工具类似,右键单击要运行自定义工具的文件和select "Run Custom Tool"

如果您想卸载扩展,请转到 "Tools -> Extensions and updates -> select your extension" 并单击卸载。请注意,在关闭 VS 之前,该工具不会被卸载。关闭后,您将弹出 window 卸载,select "Modify" 卸载。