Расширение 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; }
}
}
И вот глава моего пакета класса:
[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
) но опять без везения.
Кто-нибудь может указать мне правильное направление? Или это даже невозможно получить доступ к параметрам 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
"s Initialize()
метод (как предложил Маце), возможно, потому что я не смотрел там раньше:-)).
Так как же читать мои свойства?
4 ответа
В платформе тестового адаптера VS есть API для обмена настройками между процессами. Поскольку ничего не задокументировано, потребовалось некоторое время, чтобы понять, как использовать этот API. Рабочий пример смотрите в этом проекте GitHub.
Обновление: фреймворк vstest недавно был открыт MS.
Если вы хотите прочитать варианты из вашего пакета, вы можете просто попросить 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
как это значение данных.
Я предполагаю, что это связано с тем, что код выполняется из разных процессов
Если вы не пишете что-то явно, весь код расширяемости VS запускается в одном домене приложений под devenv.exe. Если вы видите, что он обнуляется, это означает, что либо ваш регистрационный код не запускался, либо не выполнял то, о чем вы думали.
У вас есть несколько вариантов здесь:
- Избегайте этой проблемы полностью: попробуйте рефакторинг вашего кода таким образом, чтобы код в проекте не-VSIX не должен был читать материал из среды, а передавал опции в вызов некоторого метода, и проект VSIX делает это. Зачастую это лучший результат, потому что он значительно упрощает модульное тестирование. ("Глобальное состояние" всегда плохо для тестирования.)
- Пусть ваш проект не-VSIX имеет статический класс для настройки параметров, а ваш проект VSIX ссылается на проект не-VSIX и устанавливает его при изменении настроек.
- Зарегистрируйте сервис с помощью атрибута ProvideService, и в вашем проекте, отличном от VSIX, по-прежнему вызывайте
Package.GetGlobalService
для вашего собственного типа обслуживания. Это главная оговорка, что GetGlobalService имеет кучу ошибок; Я избегаю этого метода в целом из-за проблем, связанных с ним. - Используйте MEF. Если вы знакомы с атрибутами импорта / экспорта, ваш VSIX проект может экспортировать интерфейс, определенный в вашем не-VSIX проекте, и вы импортируете его. Если вы знакомы с идеей внедрения зависимостей, это очень хорошо, но кривая обучения - это действительно скала для обучения.
Этот ответ является дополнением к моему первому ответу. Конечно, чтение параметров из зашифрованного раздела реестра может быть грязным подходом, потому что реализация проприетарного 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();
}