Visual Studio Extension: Access VS Options from arbitrary DLL

雨燕双飞 提交于 2019-12-06 14:22:46

If you want to read the options from within your package, you can just ask for an OptionPageGrid instance via the GetDialogPage method of the VSPackage. For instance:

var options = (OptionGridPage)this.package.GetDialogPage(typeof(OptionGridPage));
bool b = options.MyOption;

In case you want access the options from within another application (or an assembly which can be used without having the runtime environment of Visual Studio), you can try to read those settings right from the Windows registry, but the registry keys and values may not be present unless the options have been written by the IDE or your package. You can force persistence of the options by calling the SaveSettingsToStorage method from within your package, for instance on the first load:

options.SaveSettingsToStorage();

The settings will be stored under the following key:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\12.0\DialogPage

whereby 12.0 indicates the version of Visual Studio. Under this key you´ll find a bunch of sub-keys whose names are the full names of the DialogPage component types. Each key contains the property values; in your case you should find a REG_SZ value named MyOption having either True or False as it´s data value.

My assumption is that this is due to the fact that the code is executed from different processes

Unless you're very explicitly writing stuff otherwise, all VS extensibility code runs in a single AppDomain under devenv.exe. If you're seeing it come up null, that means either your registration code didn't run, or didn't do what you thought it did.

You have a few options here:

  1. Avoid the problem entirely: try refactoring your code such that the code in the non-VSIX project doesn't have to read stuff from the environment, but is passed options into some method call, and the VSIX project does that. This is often the best outcome, because it makes unit testing a lot easier. ("Global state" is always bad for testing.)
  2. Have your non-VSIX project just have a static class for setting the options, and your VSIX project references the non-VSIX project and sets it when the settings get changed.
  3. Register a service through the ProvideService attribute, and in your non-VSIX project still call Package.GetGlobalService for your own service type. This has the major caveat that GetGlobalService has a bunch of gotchas; I avoid that method in general due to the challenges involved with it.
  4. Use MEF. If you are familiar with Import/Export attributes, you can have your VSIX project Export an interface defined in your non-VSIX project, and you import it. If you're familiar with the idea of dependency injection this is very nice, but the learning curve is really a learning cliff.

This answer is an addition to my first answer. Of course, reading options from a spied registry key might be a dirty approach, because the implementation of the proprietary API could change in the future, which could break your extension. As Jason mentioned, you could try to "avoid the problem entirely"; for instance by cutting the functionality that reads/writes the options from/to the registry.

The DialogPage class provides the methods LoadSettingsFromStorage and SaveSettingsToStorage which are both virtual, so you can override them and call your custom persistence functionality instead.

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();
    }
}

The implementation of the ISettingsService<T> interface can be put into a shared assembly, to be referenced by the VSIX project and your stand-alone application (your test adapter assembly). The settings service could also use the Windows registry, or any other suitable storage...

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

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

The VS test adapter framework happens to have API for sharing settings accross processes. Since nothing is documented at all, it took a while to figure out how to use that API. For a working example see this GitHub project.

Update: The vstest framework has recently been open-sourced by MS.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!