外部工具Visual Studio

时间:2018-01-30 18:35:20

标签: c# .net visual-studio registry

我创建了一个要运行的应用程序,但是从Visual Studio的应用程序构建了一个外部工具。直到15.4.0应用程序运行正常,但他们修改了注册表。根据Microsoft的更改,外部工具保存在此子键下:

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时,密钥并不总是出现。

2 个答案:

答案 0 :(得分:2)

在Visual Studio 2017中,外部工具存储在用户的本地应用程序数据文件夹中的私有注册表配置单元中。如果您运行Sysinternals Process Monitor工具,您将看到Visual Studio读取/写入以\REGISTRY\A\开头的密钥 - 这就是您如何知道它的私有注册表配置单元。要更新它们,您需要通过P / Invoking RegLoadAppKey加载该注册表配置单元并附加到生成的句柄。这方面的一个例子可以在这里找到:

RegLoadAppKey working fine on 32-bit OS, failing on 64-bit OS, even if both processes are 32-bit

标题最初可能看起来有误导性,但问题中给出的示例显示了如何调用RegLoadAppKey并打开下面的子键。

您必须要面对的下一件事是找到私人注册表配置单元。 Visual Studio将私有注册表配置单元存储在用户的本地应用程序数据文件夹的子文件夹中。子文件夹名称将以Microsoft\VisualStudio\15.0_开头,然后是32位十六进制值。我不确定这个价值是什么,或者如何优雅地发现它。每个用户都有所不同。我的方法是选择以" 15.0"开头的最新文件夹。并假设它是正确的。如果有人有更好的方法来识别这个文件夹,我很乐意看到它。

我称之为版本号和十六进制字符串a"版本标记"的组合。您需要跟踪它,因为版本标记将再次用作私有注册表配置单元中的子键。

总而言之,我创建了一个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
}

答案 1 :(得分:1)

似乎您可以通过在相应VS的本地%AppData%目录的<UserCreatedTool>文件的<ExternalTools>部分的Settings/CurrentSettings.vssettings部分中添加{{1}}在VS 2017和2019中注册外部工具版本。

只需在VS中手动创建一个外部工具,然后搜索您创建的条目。

This描述了我如何发现;)