8

背景:我们有一个自定义工具,它接受 xml 输入并生成 cs 输出。自定义工具需要在 Visual Studio 中注册才能使其与该版本的 Visual Studio 一起使用。

我们做了什么:我们已经使用 Visual Studio 2015 完成了自定义工具注册,它运行良好。但现在问题出在 Visual Studio 2017 上。

问题:到目前为止,在我的研究中,我发现在 Visual Studio 2015 之前,VS 有允许注册工具的直接注册表项,但从 VS 2017 开始,微软已经改变了注册表项的存储方式(一个很好的阅读了解 VS2017 的变化)。

如果我打开 VS 2017 并尝试运行自定义工具,则会收到错误消息

在此系统上找不到自定义工具“工具名称” 。

在此处输入图像描述

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

我试图跟随这个说将.bin文件加载到注册表的人,但他也说它无法启动 VS 2017。为了启动 VS,我们必须卸载 hive。研究表明,.bin 文件可以根据安装的 VS 类型(企业版、专业版等)位于不同的位置。

有没有人这样做过?

TIA

4

2 回答 2

23

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

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

在VS2017之前创建自定义工具需要实现接口IVsSingleFileGenerator和代码在系统注册表中注册和注销自定义工具,但是在VS2017中,微软改变了整个注册表结构。变化是,VS 会将注册表项添加到私有注册表中,这样系统注册表就不会被弄乱。虽然以前注册表项是在系统注册表中创建的,但现在它们是

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

Visual Studio 2017 还支持通过从 Visual Studio 本身 (F5) 运行工具来直接测试您的工具,这会启动另一个名为Visual Studio Experimental Instance的 Visual Studio 实例,并且您的工具可以在其中进行测试,因为它使注册表项

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

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

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

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

1. 创建 VSIX 扩展 要创建扩展,您必须安装 Visual Studio 扩展性工具,该工具作为 Visual Studio 安装程序中的可选功能包含在内。如果未安装,您也可以通过修改 VS 2017 设置来安装它。通过选择创建新的 VSIX(Visual Studio 扩展)项目

新项目 -> 可扩展性 -> VSIX 项目

给它起一个名字,比如“CountLinesVSIX”。

2. 添加新的 Visual Studio 包 创建 VSIX 项目后,通过选择向其添加新的 Visual Studio 包

添加 -> 新项目 -> 可扩展性 -> Visual Studio 包

将其命名为“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")]。可以通过选择"Tools -> Create GUID"从 VS2017 创建 GUID 。选择 GUID 格式作为注册表格式。请注意,上述代码中提到的 GUID 没有大括号。

[ComVisible(true)]COM 互操作需要

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

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

4.添加注册表项代码 使用以下代码创建新的类文件,将其命名为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”中添加“安装目标”,以确保您的扩展支持不同的 VS2017 版本。在 VS 2017 中运行您的工具以测试它是否按预期工作。运行 VSIX 后,Visual Studio 实验实例将安装扩展并将其注册到注册表“C:\Users\xyz\AppData\Local\Microsoft\VisualStudio\15.0_xxExp\privateregistry.bin”中。您可以通过选择“工具 -> 扩展和更新”来查看已安装的扩展。要测试该工具,我们必须打开一个虚拟项目,在解决方案资源管理器中选择一个文件,转到其属性并将自定义工具属性更新为“CountLines”。完成此操作后,VS 将在后台运行该工具并生成输出,在我们的示例中,它将在所选文件下生成一个 xml 文件。或者,

6. 双击生成的.VSIX 文件安装工具 测试成功后,尝试安装VSIX,可以在“projectName/bin/debug”位置找到。通过双击文件安装 VSIX,按照安装步骤进行操作。现在您的工具将可以在 VS2017 中使用。使用工具类似,右键单击要运行自定义工具的文件,然后选择“运行自定义工具”

如果您想卸载扩展,请转到“工具 -> 扩展和更新 -> 选择您的扩展”,然后单击卸载。请注意,在 VS 关闭之前,该工具不会被卸载。关闭后会弹出卸载窗口,选择“修改”进行卸载。

于 2018-05-28T12:35:56.900 回答
3

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

解决方案:

  1. 我们必须加载 .bin 文件(通过加载 hiv)。
  2. 进行更改或编辑 bin 以注册您的工具。
  3. 卸载蜂巢。

步骤#1:加载 Hive。

a) 打开注册表(regedit)。选择节点HKEY_LOCAL_MACHINE

b) 转到 | 文件 -> 加载 Hive

c) 选择位于 -> 的 bin 文件%LocalAppData%\ Microsoft\ VisualStudio\ 15.0_'instance_id'\privateregistry.bin

d) 提供密钥名称。HKEY_LOCAL_MACHINE这将使用您的密钥名称创建一个新的密钥条目。

e) 您可以在以下位置检查 .bin 文件HKEY_LOCAL_MACHINE\YourKeyName\Software\Microsoft\VisualStudio\

在此处输入图像描述

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

步骤#3:卸载 Hive。

a) 在 下选择您的密钥HKEY_LOCAL_MACHINE

b) 转到 | 文件菜单

c) 卸载 Hive。

在此处输入图像描述

于 2018-05-25T11:29:52.853 回答