Visual Studio扩展:从任意DLL

时间:2015-08-24 09:00:45

标签: visual-studio visual-studio-extensions vsix envdte vspackage

我目前正在开发我的第一个VS扩展,它需要为用户提供一些选项。在https://msdn.microsoft.com/en-us/library/bb166195.aspx之后,我很容易想出自己的选项页面。但是,我还没有找到如何阅读我的选择。

我的扩展的解决方案结构如下:

MySolution
  MyProject (generates a DLL from C# code)
  MyProjectVSIX

按照上面引用的教程,我在VSIX项目中添加了一个VS Package并按照描述对其进行了配置。因此,带有我的选项的选项页面显示在“工具/选项”下。太好了!这是我的DialogPage实施:

public class OptionPageGrid : DialogPage
{
    private bool myOption = false;

    [Category(Options.CATEGORY_NAME)]
    [DisplayName("My option")]
    [Description("Description of my option.")]
    public bool MyOption
    {
        get { return myOption; }
        set { myOption = value; }
    }
}

这是我的Package类的负责人:

[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]     [Guid(MyOptionsPage.PackageGuidString)]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
[ProvideOptionPage(typeof(OptionPageGrid), Options.CATEGORY_NAME, Options.PAGE_NAME, 0, 0, true)]
public sealed class MyOptionsPage : Package, IOptions
{
    ...

但是,我现在想要阅读这些选项,我想从MyProject(它没有MyProjectVSIX的依赖)来做。这就是我失去的地方。我的第一次尝试是让我的Package实现一个IOptions接口,让它通过调用Options.Register(IOptions)构造函数中的静态方法Package来注册自己。这是有效的(即,Register()中的断点被命中),但是当我尝试读取选项时,静态IOptions实例仍为空。我的假设是,这是因为代码是从不同的进程执行的(这是我无法控制的)。

经过一些谷歌搜索,我试图得到一个DTE对象的实例(如果我正确理解的话,这将允许我阅读我的选项),但没有成功。我尝试了多种变体,包括https://msdn.microsoft.com/en-us/library/ee834473.aspx

中描述的变种
DTE Dte = Package.GetGlobalService(typeof(DTE)) as DTE;

我总是以空引用结束。

最后,由于教程建议通过Package的实例访问选项,我试图弄清楚如何通过某种注册表获取我的VS Package的这样一个实例(我不过可以投到IOptions),但又没有运气。

任何人都可以指出我正确的方向吗?或者甚至无法从非VSIX项目访问VS选项?

更新:我做了更多研究,缺少一条信息:我的扩展程序是一个单元测试适配器。这似乎意味着测试发现代码以及测试执行代码是从不同的进程运行的,即我的假设是正确的。

我在此期间设法访问我正在运行的VS实例的DTE对象(我会在问题解决后立即用我的完整解决方案发布),但仍然有访问选项的问题。实际上,以下代码(从这里复制:https://msdn.microsoft.com/en-us/library/ms165641.aspx)可以很好地工作:

Properties txtEdCS = DTEProvider.DTE.get_Properties("TextEditor", "CSharp");
Property prop = null;
string msg = null;
foreach (EnvDTE.Property temp in txtEdCS)
{
    prop = temp;
    msg += ("PROP NAME: " + prop.Name + "   VALUE: " + prop.Value) + "\n";
}
MessageBox.Show(msg);

但是,如果我按如下方式更改上述内容:

Properties txtEdCS = DTEProvider.DTE.get_Properties(CATEGORY_NAME, PAGE_NAME);

现在代码崩溃了。奇怪的是,我可以在HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\14.0Exp_Config\AutomationProperties\My Test Adapter\General下的注册表中看到我的属性类别和页面。搜索我的属性会在HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\14.0Exp\ApplicationPrivateSettings\MyProjectVSIX\OptionPageGrid下显示它们(mabye,因为我添加了

OptionPageGrid Page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
Page.SaveSettingsToStorage();

采用Package的{​​{1}}方法(正如Matze所建议的那样),也许是因为我以前没有看过那里:-))。

那么如何阅读我的属性?

3 个答案:

答案 0 :(得分:2)

如果您想要阅读包中的选项,可以通过OptionPageGrid的{​​{1}}方法申请GetDialogPage个实例。例如:

VSPackage

如果您想从另一个应用程序(或可以在没有Visual Studio运行时环境的情况下使用的程序集)中访问选项,您可以尝试直接从Windows注册表中读取这些设置,但是注册表项和除非选项已由var options = (OptionGridPage)this.package.GetDialogPage(typeof(OptionGridPage)); bool b = options.MyOption; 或您的包裹写入,否则可能不会出现值。您可以通过从包中调用IDE方法强制执行选项的持久性,例如在第一次加载时:

SaveSettingsToStorage

设置将存储在以下键下:

options.SaveSettingsToStorage();

其中HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\12.0\DialogPage 表示Visual Studio的版本。在此密钥下,您将找到一组子密钥,其名称是12.0组件类型的全名。每个键包含属性值;在您的情况下,您应该找到名为DialogPage的{​​{1}}值,其值为REG_SZMyOption

答案 1 :(得分:1)

这个答案是我第一个答案的补充。当然,从间谍注册表项中读取选项可能是一种肮脏的方法,因为专有API的实现将来可能会发生变化,这可能会破坏您的扩展。正如杰森所说,你可以试着“完全避免这个问题”;例如,通过削减从/向注册表读取/写入选项的功能。

DialogPage类提供的方法LoadSettingsFromStorageSaveSettingsToStorage都是虚拟的,因此您可以覆盖它们并调用自定义持久性功能。

public class OptionPageGrid : DialogPage
{
    private readonly ISettingsService<OptionPageGridSettings> settingsService = ...

    protected override IWin32Window Window
    {
        get
        {
            return new OptionPageGridWindow(this.settingsService);
        }
    }

    public override void LoadSettingsFromStorage()
    {
        this.settingsService.LoadSettingsFromStorage();
    }

    public override void SaveSettingsToStorage()
    {
        this.settingsService.SaveSettingsToStorage();
    }
}

ISettingsService<T>接口的实现可以放入共享程序集中,由VSIX项目和独立应用程序(测试适配器程序集)引用。设置服务还可以使用Windows注册表或任何其他合适的存储...

public class WindowsRegistrySettingsService<T> : ISettingsService<T> 
{
    ...
}

public interface ISettingsService<T>
{
    T LoadSettingsFromStorage();
    void SaveSettingsToStorage();
}

答案 2 :(得分:1)

VS测试适配器框架恰好具有用于在进程间共享设置的API。由于没有记录任何内容,因此需要一段时间才能弄清楚如何使用该API。有关工作示例,请参阅this GitHub project

更新:vstest框架最近由MS开源。