如何向 Visual Studio 2017 或 Visual Studio 2019 注册“自定义工具”以使其正常工作?

2024-04-09

背景:我们有一个定制工具 https://msdn.microsoft.com/en-us/library/bb166817.aspx它接受 xml 输入并生成 cs 输出。自定义工具需要注册 https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/registering-single-file-generators与 Visual Studio 一起使用,以便使其与该版本的 Visual Studio 配合使用。

我们所做的:我们已经做到了自定义工具注册 https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/registering-single-file-generators与 Visual Studio 2015 一起工作正常。但现在问题出在 Visual Studio 2017 上。

Problem:到目前为止,在我的研究中,我发现直到 Visual Studio 2015,VS 都有允许注册该工具的直接注册表项,但从 VS 2017 开始,微软对注册表项的存储方式进行了更改(了解 VS2017 中的变化的好读物 http://www.visualstudioextensibility.com/2017/07/15/about-the-new-privateregistry-bin-file-of-visual-studio-2017/).

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

找不到自定义工具「工具名称」在这个系统上。

这是显而易见的,因为自定义工具尚未在 VS 2017 中注册才能工作。

我试着跟随this guy https://github.com/Microsoft/VSProjectSystem/blob/master/doc/overview/examine_registry.md上面说要加载.bin文件写入注册表,但他还说它无法启动 VS 2017。为了启动 VS,我们必须卸载配置单元。研究表明,.bin 文件可以根据安装的 VS 类型(企业版、专业版等)位于不同的位置。

以前有人这样做过吗?

TIA


您可能需要通过创建 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 实例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[Guid("202E7E8B-557E-46CB-8A1D-3024AD68F44A")]。可以通过选择从 VS2017 创建 GUID“工具->创建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,该 VSIX 位于“projectName/bin/debug”位置。双击该文件安装 VSIX,按照安装步骤操作。现在您的工具将可以在 VS2017 中使用。使用工具类似,右键单击要运行自定义工具的文件,然后选择“运行自定义工具”

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何向 Visual Studio 2017 或 Visual Studio 2019 注册“自定义工具”以使其正常工作? 的相关文章

随机推荐

  • SwiftUI:停止永远重复的动画

    我想在屏幕上有一个类似 徽章 的东西 当满足条件时 它会从正常尺寸弹到更大 然后反复恢复到正常尺寸 直到不再满足条件 不过 我似乎无法让徽章停止 弹跳 一旦开始 就势不可挡 我尝试过的 我尝试过使用一些动画 但它们可以分为使用 repeat
  • Android 版 OpenCV:示例项目 ClassNotFound 异常

    我正在尝试运行适用于 Android 的 opencv 示例 它不起作用 java lang RuntimeException Unable to instantiate activity ComponentInfo org opencv
  • Rake / Rspec:如何使用 --pattern 抑制/安静/静音显示命令的第一条输出行?

    Problem 如果我跑ServerSpec 基于RSpec 通过Rake使用以下命令之一 rake rake spec rake spec all rake spec
  • 将字符串拆分为数组数组[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我正在写一个iOS App in 斯威夫特 4 2 来自服务器的响应是一个字符串 其值由管道字符 分隔 它包含许多行值
  • Bash:如何仅在完整的行中复制交互式脚本的输入/输出?

    如何实时捕获脚本的输入 输出 例如使用 tee 但逐行而不是逐个字符 我的目标是仅在退格键和自动完成完成处理后 按下 RETURN 键后 捕获输入到脚本交互式提示中的输入 具体来说 我正在尝试为 ssh 创建一个包装器脚本 该脚本创建远程服
  • 无法从 SwingWorker 类更新 JProgressBar

    我有我的主 GUI 线程 其中有一个 JprogressBar 并正在实现 ProprtyChangeListener 当按下按钮时 扩展 SwingWorker 的不同类就会启动并执行一系列可能很长的计算 我需要 A 类中的进度条来根据
  • npm 崩溃并显示“ERR!cb() 从未调用”

    我试图使用安装 vue cli npm install g vue cli 我收到以下错误 Unhandled rejection Error EACCES permission denied mkdir home moeketsi npm
  • Android ffmpeg 简单 JNI 包装器

    我一直在尝试使用带有命令行访问的 ffmpeg 二进制文件一段时间 但一无所获 使用runtime exec 看起来我能够让它工作的唯一方法是使用 C 中的包装器来使用 JNI 访问构建的 ffmpeg 库 主要问题 我已经有超过五年没有编
  • Guava 地图中的驱逐惰性

    当前的地图驱逐算法相当懒惰 看起来过期的对象只有在访问数据结构时才会被驱逐 例如 从地址到索引器的映射定义为 ConcurrentMap
  • 将控件定位在复选框的中间

    这是我之前问题的后续 依赖于字体的控制定位 https stackoverflow com questions 37306 font dependent control positioning 这是试图解决这个问题real这个问题背后的问题
  • BigTable中的布隆过滤器可以仅根据行ID进行过滤吗?

    BigTable 使用布隆过滤器来允许点读取 以避免访问给定键列对内不包含任何数据的 SSTable 如果查询只指定行 ID 而没有列 ID 这些布隆过滤器是否也可用于避免访问 SSTable BigTable 使用行列对作为插入其布隆过滤
  • 检测ES模块是否在Node中从命令行运行

    在 Node 中使用 CommonJS 模块时 您可以使用以下命令检测脚本是否正在从命令行运行require main module 在 Node 中使用 ES 模块时 检测脚本是否正在从命令行运行的等效方法是什么 使用 experimen
  • 套接字编程Python:如何确保收到完整消息?

    我正在使用 python 3 x 和套接字模块 服务器在 ipv4 地址上运行并使用 tcp 我阅读了一些有关如何发送和接收数据的教程 对于服务器或客户端 要确保发送整个消息 您可以简单地检查发送的数据量是否等于消息的大小 def myse
  • 将 Foreach 分成线程示例

    我想跑 SearchResultByOrderNumber string orderNumber 中的方法Foreach与多线程 有十个订单号OrderNumbers数据表 在搜索这些 OrderNumbers 时OrderResultsD
  • 为什么 babel 将“true”转换为“!0”

    我检查了我转换后的代码 我看到了true被转换为 0 Example let obj loading true 转换为 let obj loading 0 这是为什么 是因为性能原因还是其他原因 对于小型化 即 更小的包大小 也可以看看 b
  • Python 委托模式 - 如何避免循环引用?

    我想问在Python中使用委托模式是否会导致循环引用 如果是的话 实现它以确保对象及其委托将被垃圾收集的最佳方法是什么 在 Objective C 中 通过使用委托的弱引用来避免上述问题 在 C 中 我们不会对委托调用删除 我在这里找到了
  • AWS 在 Google 云 (GCP) 中承担同等角色吗?

    如何在GCP中设置多帐户 项目 在AWS中可以通过使用assume role 有人知道如何在 Google Cloud Platform GCP 中执行此操作吗 我尝试在 GCP 中探索 AWS 等效项 但找不到任何文档 据记载 承担角色
  • WPF DropShadowEffect - 意外的色差

    我有一个带有 DropShadowEffect 的 TextBlock 某些阴影颜色显示得比指定的颜色更深 几乎就像它们被染色一样 然而 并非每种颜色都会发生这种情况 有谁知道为什么 或者有纠正的方法吗 这是一个非常基本的示例
  • 从网络浏览器中检测和访问 USB 设备 - 即使用插件

    是否可以通过插件 activeX Java 或 Flex 编写一个能够检测从 USB 设备通道读取数据流的浏览器插件 我做了一些研究 但没有找到答案 http support microsoft com kb 832678 http sup
  • 如何向 Visual Studio 2017 或 Visual Studio 2019 注册“自定义工具”以使其正常工作?

    背景 我们有一个定制工具 https msdn microsoft com en us library bb166817 aspx它接受 xml 输入并生成 cs 输出 自定义工具需要注册 https learn microsoft com