Расширение Visual Studio: доступ к параметрам VS из произвольной библиотеки DLL

В настоящее время я разрабатываю свое первое расширение VS, которое должно предоставить пользователю некоторые параметры. После https://msdn.microsoft.com/en-us/library/bb166195.aspx, было довольно легко создать собственную страницу параметров. Однако пока не научился читать свои варианты.

Структура решения моего расширения выглядит следующим образом:

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

Следуя приведенному выше руководству, я добавил VS Package в свой проект VSIX и настроил его, как описано. В результате моя страница параметров с моими параметрами отображается в разделе Инструменты / Параметры. Отлично! Вот моя 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 по-прежнему имеет значение null. Я предполагаю, что это связано с тем, что код выполняется из разных процессов (что находится вне моего контроля).

После еще нескольких поисков в Google я попытался получить экземпляр объекта DTE (который позволил бы мне прочитать мои параметры, если я правильно понял), но безуспешно. Я пробовал несколько вариантов, в том числе описанный на https://msdn.microsoft.com/en-us/library/ee834473.aspx и

DTE Dte = Package.GetGlobalService(typeof(DTE)) as DTE;

Я всегда получаю нулевую ссылку.

Наконец, поскольку в руководстве предлагалось получить доступ к параметрам через экземпляр Package, я попытался выяснить, как получить такой экземпляр моего VS Package через какой-то реестр (который я мог затем преобразовать в IOptions), но снова безуспешно .

Кто-нибудь может указать мне правильное направление? Или вообще невозможно получить доступ к параметрам VS из проекта, отличного от VSIX?

Обновление: я провел еще несколько исследований, и одна часть информации была пропущена: мое расширение - это адаптер модульного тестирования. Похоже, это означает, что код обнаружения теста, а также код выполнения теста запускаются из разных процессов, т.е. мое предположение было правильным.

Тем временем мне удалось получить доступ к объекту DTE экземпляра VS, в котором я работаю (я опубликую его со своим полным решением, как только моя проблема будет решена), но все еще имею проблемы с доступом к параметрам. Фактически, следующий код (скопированный отсюда: 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 (мэбай, потому что я добавил

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

к методу Package Initialize() (как предложил Матце), может быть, потому, что я не заглядывал туда раньше :-)).

Итак, как читать мои свойства?


person csoltenborn    schedule 24.08.2015    source источник


Ответы (4)


Если вы хотите прочитать параметры из вашего пакета, вы можете просто запросить экземпляр OptionPageGrid с помощью GetDialogPage метода VSPackage. Например:

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

Если вы хотите получить доступ к параметрам из другого приложения (или сборки, которая может использоваться без среды выполнения Visual Studio), вы можете попытаться прочитать эти настройки прямо из реестра Windows, но ключи и значения реестра могут быть недоступны. присутствовать, если опции не были написаны IDE или вашим пакетом. Вы можете принудительно сохранить параметры, вызвав метод SaveSettingsToStorage из вашего пакета, например, при первой загрузке:

options.SaveSettingsToStorage();

Настройки будут сохранены под следующим ключом:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\12.0\DialogPage

где 12.0 указывает версию Visual Studio. Под этим ключом вы найдете группу подключей, имена которых являются полными именами DialogPage типов компонентов. Каждый ключ содержит значения свойств; в вашем случае вы должны найти REG_SZ значение с именем MyOption, имеющее либо True, либо False в качестве значения данных.

person Matze    schedule 24.08.2015
comment
Мы надеемся когда-нибудь отказаться от постоянства реестра, поэтому лучше всего вызывать через различные API, если вы вообще можете. - person Jason Malinowski; 24.08.2015
comment
Я думал о прямом доступе к реестру, но хотел оставить это как самый последний вариант ... Способ доступа к моей PropertyPage через его пакет также упоминается в статье MS, которую я цитировал - проблема в том, как получить доступ к этому пакету изнутри мой собственный код. Оказалось, что мне действительно приходится иметь дело с несколькими процессами - см. Мой обновленный вопрос ... - person csoltenborn; 25.08.2015
comment
Обратите внимание, что последние версии VS хранят настройки в частном кусте реестра, который доступен только в процессе VS. - person Dark Daskin; 28.02.2021
comment
@DarkDaskin Интересно; Я проверю это. - person Matze; 01.03.2021

Я предполагаю, что это связано с тем, что код выполняется из разных процессов.

Если вы явно не пишете что-то иное, весь код расширяемости VS выполняется в одном домене приложения под devenv.exe. Если вы видите, что он оказался пустым, это означает, что либо ваш регистрационный код не сработал, либо не сделал то, что вы думали.

Здесь у вас есть несколько вариантов:

  1. Полностью избегайте проблемы: попробуйте провести рефакторинг вашего кода так, чтобы код в проекте, отличном от VSIX, не считал данные из среды, а передавал параметры в вызов некоторого метода, и проект VSIX это делает. Часто это лучший результат, потому что он значительно упрощает модульное тестирование. («Глобальное состояние» всегда плохо для тестирования.)
  2. Пусть ваш проект, не относящийся к VSIX, имеет просто статический класс для настройки параметров, а ваш проект VSIX ссылается на проект, не относящийся к VSIX, и устанавливает его при изменении параметров.
  3. Зарегистрируйте службу с помощью атрибута ProvideService, и в своем проекте, отличном от VSIX, по-прежнему вызывайте Package.GetGlobalService для своего типа службы. Здесь есть главное предостережение: у GetGlobalService есть множество подводных камней; Я вообще избегаю этого метода из-за проблем, связанных с ним.
  4. Используйте MEF. Если вы знакомы с атрибутами импорта / экспорта, у вас может быть ваш проект VSIX Export интерфейс, определенный в вашем проекте, отличном от VSIX, и вы его импортируете. Если вы знакомы с идеей внедрения зависимостей, это очень хорошо, но кривая обучения действительно требует обучения.
person Jason Malinowski    schedule 24.08.2015
comment
Спасибо за отличные рекомендации, но я забыл одну часть информации, которую добавил - похоже, мое предположение было правильным. Я из мира Java, поэтому мне потребовалось некоторое время, чтобы понять, что происходит - извините, что не спросил более точного ... Несмотря на это, я, скорее всего, из интереса посмотрю на MEF, - person csoltenborn; 25.08.2015
comment
О, тот факт, что вы работаете в другом процессе, значительно меняет это. В общем, все описанные мной методы чтения свойств работают только в рамках одного процесса, поэтому вам нужно каким-то образом передавать информацию. Я не знаком с расширяемостью тестовых адаптеров, чтобы знать, есть ли у них дизайн для этого или нет. Обратите внимание, что вы можете встретить код или рекомендации по использованию DTE для чтения ваших параметров из другого процесса, что можно сделать. Я бы не рекомендовал это, так как вы рискуете попасть в тупик. - person Jason Malinowski; 25.08.2015
comment
Хорошо, спасибо, что сообщили мне. Похоже, я застрял в чтении опций из реестра. Однако, поскольку мне нужно только их прочитать (а в настоящее время я планирую только поддержку VS2015 и последующих версий), это не должно быть так уж плохо. По-прежнему странно, что в инфраструктуре адаптера модульного тестирования нет API для подобных вещей ... - person csoltenborn; 25.08.2015
comment
Завтра я отправлю электронное письмо внутри компании, чтобы узнать, смогу ли я кого-нибудь найти, но это займет несколько дней, учитывая, что команды находятся в разных часовых поясах ... - person Jason Malinowski; 26.08.2015
comment
Было бы здорово, еще раз спасибо! Обратите внимание, что меня больше всего заинтересует общая документация по фреймворку Test Adapter - я не нашел никакой официальной документации API, только некоторые записи в блоге ... - person csoltenborn; 26.08.2015

Этот ответ является дополнением к моему первому ответу. Конечно, чтение параметров из шпионского раздела реестра может быть грязным подходом, потому что реализация проприетарного API может измениться в будущем, что может сломать ваше расширение. Как сказал Джейсон, вы можете попытаться «полностью избежать проблемы»; например, путем сокращения функции чтения / записи параметров из / в реестр.

Класс DialogPage предоставляет методы LoadSettingsFromStorage и SaveSettingsToStorage, которые являются виртуальными, поэтому вы можете переопределить их и вместо этого вызвать свои собственные функции сохранения.

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();
}
person Matze    schedule 28.08.2015

Фреймворк тестового адаптера VS имеет API для обмена настройками между процессами. Поскольку вообще ничего не документировано, потребовалось время, чтобы понять, как использовать этот API. Рабочий пример см. В этом проекте GitHub.

Обновление: платформа vstest недавно была открыта MS.

person csoltenborn    schedule 25.03.2016