外部工具 Visual Studio

3
我创建了一个应用程序来运行,但需要从该应用程序中为Visual Studio构建一个外部工具。在15.4.0之前,该应用程序一直正常工作,但是他们修改了注册表。根据微软的更改,外部工具保存在此子键下:SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools
当您观察该键时,您会清楚地看到工具、Guid、错误查找和其他几个包含在Visual Studio中的文件夹(子键)。然而,问题在于,如果您手动在Visual Studio中创建工具,则将关键信息直接转储到根外部工具键中。
因此,如果我采取以下任何一种方法:
var user = RegistryKey.OpenBaseKey(RegistryHive.CurrenUser, RegistryView.Default);
var tools = user.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools", true);
var path = Environment.GetCommandLineArgs()[0];

tools.SetValue("ToolArg", "$(ItemPath)");
tools.SetValue("ToolCmd", path);
tools.SetValue("ToolDir", String.Empty);
tools.SetValue("ToolOpt", 0x12);
tools.SetValue("ToolsSourceKey", String.Empty);
tools.SetValue("ToolTitle", "My Tool");

    var user = RegistryKey.OpenBaseKey(RegistryHive.CurrenUser, RegistryView.Default);
    var tools = user.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools", true);
var sub = tools.CreateSubKey("MyTool");
    var path = Environment.GetCommandLineArgs()[0];

sub.SetValue("ToolArg", "$(ItemPath)");
sub.SetValue("ToolCmd", path);
sub.SetValue("ToolDir", String.Empty);
sub.SetValue("ToolOpt", 0x12);
sub.SetValue("ToolsSourceKey", String.Empty);
sub.SetValue("ToolTitle", "My Tool");

工具未出现在列表或工具栏中。 Visual Studio 2017 15.5.* 与代码不同的地方是什么,导致它不能正常工作?更糟糕的是,即使在Visual Studio 2017手动创建键时,有时也无法看到它。

参见:https://dev59.com/9FgQ5IYBdhLWcg3wWCjP - Ian Kemp
2个回答

5
在Visual Studio 2017中,外部工具存储在用户本地应用程序数据文件夹中的私有注册表中。如果您运行Sysinternals Process Monitor工具,您会看到Visual Studio读/写一个以 \REGISTRY\A\ 开头的键 - 这是您知道它是一个私有注册表。要更新它们,您需要通过P/Invoke RegLoadAppKey 加载该注册表并附加到生成的句柄。可以在此处找到相关示例:

RegLoadAppKey在32位操作系统上正常工作,在64位操作系统上失败,即使两个进程都是32位的

标题可能首先看起来有些误导,但问题中给出的示例显示了如何调用RegLoadAppKey并打开子键。
接下来你需要处理的问题是找到私有注册表。Visual Studio将私有注册表存储在用户本地应用程序数据文件夹的子文件夹中。子文件夹名称将以 Microsoft\VisualStudio\15.0_ 开头,然后是一个32位的十六进制值。我不确定那个值是什么,或者如何优雅地发现它。对于每个用户,它都是不同的。我的方法是选择以“15.0”开头的最新文件夹,并假设它是正确的。如果有更好的方式来识别此文件夹,我会很乐意看到。
我称版本号和十六进制字符串的组合为“版本标记”。您需要跟踪它,因为版本标记将再次用作私有注册表中的子键。
将所有内容组合在一起,我创建了一个名为VisualStudioContext的类,它可以定位私有注册表并加载根键。
public class VisualStudioContext : IDisposable
{
    public string VersionTag { get; }
    public string UserFolder { get; }
    public string PrivateRegistryPath { get; }
    public SafeRegistryHandle RegistryHandle { get; }
    public RegistryKey RootKey { get; }

    private static readonly Lazy<VisualStudioContext> LazyInstance = new Lazy<VisualStudioContext>(() => new VisualStudioContext());

    public static VisualStudioContext Instance => LazyInstance.Value;

    private VisualStudioContext()
    {
        try
        {
            string localAppDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            string vsFolder = $"{localAppDataFolder}\\Microsoft\\VisualStudio";
            var vsFolderInfo = new DirectoryInfo(vsFolder);
            DateTime lastDateTime = DateTime.MinValue;

            foreach (DirectoryInfo dirInfo in vsFolderInfo.GetDirectories("15.0_*"))
            {
                if (dirInfo.CreationTime <= lastDateTime)
                    continue;

                UserFolder = dirInfo.FullName;
                lastDateTime = dirInfo.CreationTime;
            }

            if (UserFolder == null)
                throw new Exception($"No Visual Studio folders found in \"{vsFolder}\"");
        }
        catch (Exception ex)
        {
            throw new Exception("Unable to open Visual Studio folder", ex);
        }

        VersionTag = Path.GetFileName(UserFolder);

        PrivateRegistryPath = $"{UserFolder}\\privateregistry.bin";
        int handle = RegistryNativeMethods.RegLoadAppKey(PrivateRegistryPath);

        RegistryHandle = new SafeRegistryHandle(new IntPtr(handle), true);
        RootKey = RegistryKey.FromHandle(RegistryHandle);
    }

    public void Dispose()
    {
        RootKey?.Close();
        RegistryHandle?.Dispose();
    }

    public class Exception : ApplicationException
    {
        public Exception(string message) : base(message)
        {
        }

        public Exception(string message, Exception innerException) : base(message, innerException)
        {
        }
    }

    internal static class RegistryNativeMethods
    {
        [Flags]
        public enum RegSAM
        {
            AllAccess = 0x000f003f
        }

        private const int REG_PROCESS_APPKEY = 0x00000001;

        // approximated from pinvoke.net's RegLoadKey and RegOpenKey
        // NOTE: changed return from long to int so we could do Win32Exception on it
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern int RegLoadAppKey(String hiveFile, out int hKey, RegSAM samDesired, int options, int reserved);

        public static int RegLoadAppKey(String hiveFile)
        {
            int hKey;
            int rc = RegLoadAppKey(hiveFile, out hKey, RegSAM.AllAccess, REG_PROCESS_APPKEY, 0);

            if (rc != 0)
            {
                throw new Win32Exception(rc, "Failed during RegLoadAppKey of file " + hiveFile);
            }

            return hKey;
        }
    }
}

你可以这样使用它来打开“外部工具”键:
using (var context = VisualStudioContext.Instance)
{
    RegistryKey keyExternalTools = 
        context.RootKey.OpenSubKey($"Software\\Microsoft\\VisualStudio\\{context.VersionTag}\\External Tools", true);

    // Do something interesting here
}

仍然无法相信他们在中途更改了这个。 - Greg

2
似乎您可以通过在本地%AppData%目录中的VS版本的Settings/CurrentSettings.vssettings文件的<ExternalTools>部分中添加<UserCreatedTool>来注册VS 2017和2019中的外部工具。只需手动在VS中创建一个外部工具,然后搜索您创建的条目即可。这里有一篇文章描述了我是如何发现这个方法的 ;):链接

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接