工作区是 Visual Studio 用来表示在 Open Folder 中的任意文件集合的一种方式,由 IWorkspace 类型表示。 工作区本身不了解与文件夹中的文件相关的内容或功能。 相反,它为功能和扩展提供了一组通用的 API,用于生成和使用其他人可以处理的数据。 生成者使用各种导出属性通过 Managed Extensibility Framework (MEF) 进行组合。
工作区提供程序和服务
工作区提供者和服务提供数据和功能,以便对工作区内容做出反应。 它们可能提供上下文文件信息、源文件中的符号或生成功能。
这两个概念都使用 工厂模式,并由工作区通过 MEF 导入。 所有导出属性都实现了 IProviderMetadataBase 或 IWorkspaceServiceFactoryMetadata,但扩展应为导出的类型使用特定的具体类型。
提供程序和服务之间的一个区别在于它们与工作区的关系。 工作区可以有多个特定类型的提供程序,但每个工作区只创建一个特定类型的服务。 例如,工作区有许多文件扫描程序提供商,但每个工作区只有一个索引服务。
另一个关键区别在于对提供程序和服务的数据的使用。 工作区是获取提供者数据的切入点,原因有几个。 首先,提供者通常会创建一些有限的数据集。 数据可能是 C# 源文件的符号,也可能是 CMakeLists.txt 文件的生成文件上下文。 工作区将消费者的请求与元数据符合请求的提供程序进行匹配。 其次,某些方案允许许多提供程序参与请求,而其他方案则使用优先级最高的提供程序。
相比之下,扩展可以获取工作区服务的实例并直接与工作区服务交互。
IWorkspace 上的扩展方法可用于 Visual Studio 提供的服务,例如 GetFileWatcherService。 您的扩展可能会为扩展中的组件或供其他扩展使用提供工作区服务。 使用者应使用 GetServiceAsync 或你在 IWorkspace 类型上提供的扩展方法。
警告
不要创作与 Visual Studio 冲突的服务。 这可能会导致意外问题。
工作区关闭时的处置
在关闭工作区时,扩展器可能需要处置,但会调用异步代码。 可以使用 IAsyncDisposable 接口轻松编写此代码。
相关类型
- IWorkspace 是打开的工作区(如打开的文件夹)的中心实体。
- IWorkspaceProviderFactory<T> 为每个已实例化的工作区创建服务提供者。
- IWorkspaceServiceFactory 为每个实例化工作区创建一个服务。
- 应在处理过程中需要运行异步代码的提供程序和服务上实现 IAsyncDisposable。
- WorkspaceServiceHelper 提供用于访问已知服务或任意服务的帮助程序方法。
工作区设置
工作区具有一个 IWorkspaceSettingsManager 服务,可对工作区进行简单但强大的控制。 有关设置的基本概述,请参阅 自定义生成和调试任务。
大多数 SettingsType 类型的设置都是 .json 文件,例如 VSWorkspaceSettings.json 和 tasks.vs.json。
工作区设置的强大功能集中于“范围”,而这些范围不过是工作区中的路径。 当使用者调用 GetAggregatedSettings 时,将聚合包含请求的路径和设置类型的所有范围。 范围聚合优先级如下所示:
- “本地设置”,通常是工作区根目录
.vs目录。 - 请求的路径本身。
- 请求路径的父目录。
- 工作区根目录以内(包括根目录)的所有其他上级目录。
- “全局设置”,位于用户目录中。
结果是 IWorkspaceSettings 的实例。 此对象保存特定类型的设置,可以查询存储为 string的设置键名称。
GetProperty 方法和 WorkspaceSettingsExtensions 扩展方法要求调用方知道所请求的设置值的类型。 由于大多数设置文件保留为 .json 文件,因此许多调用将使用这些类型的 string、bool、int和数组。 还支持对象类型。 在这些情况下,可以使用 IWorkspaceSettings 本身作为类型参数。 例如:
{
"intValue": 1,
"stringValue": "s",
"boolValue": true,
"stringArray": [
"s1",
"s2"
],
"nestedIWorkspaceSettings": {
"nestedString": "ns"
}
}
假设这些设置位于用户的 VSWorkspaceSettings.json中,则可以按以下方式访问数据:
using System.Collections.Generic;
using Microsoft.VisualStudio.Workspace;
using Microsoft.VisualStudio.Workspace.Settings;
private static void ReadSettings(IWorkspace workspace)
{
IWorkspaceSettingsManager settingsManager = workspace.GetSettingsManager();
IWorkspaceSettings settings = settingsManager.GetAggregatedSettings(SettingsTypes.Generic);
// result == WorkspaceSettingsResult.Success
WorkspaceSettingsResult result = settings.GetProperty("intValue", out int intValue);
result = settings.GetProperty("stringValue", out string stringValue);
result = settings.GetProperty("boolValue", out bool boolValue);
result = settings.GetProperty("stringArray", out string[] stringArray);
result = settings.GetProperty("nestedIWorkspaceSettings", out IWorkspaceSettings nestedIWorkspaceSettings);
result = nestedIWorkspaceSettings.GetProperty("nestedString", out string nestedString);
// Extension method alternative using default values.
int intValueOrDefault = settings.Property("intValue", /* default */ 42);
// Missing key. result == WorkspaceSettingsResult.Undefined
result = settings.GetProperty("missing", out string missing);
// Wrong type for a key. result == WorkspaceSettingsResult.Error
result = settings.GetProperty("intValue", out IWorkspaceSettings notSettings);
// Special ability to union "stringArray" across all scopes.
IEnumerable<string> allStringArray = settings.UnionPropertyArray<string>("stringArray");
}
备注
这些设置 API 与 Microsoft.VisualStudio.Settings 命名空间中可用的 API 无关。 工作区设置与主机无关,并使用特定于工作区的设置文件或动态设置提供程序。
提供动态设置
扩展可以提供 IWorkspaceSettingsProvider。 这些内存提供程序允许扩展添加设置或覆盖其他设置。
导出 IWorkspaceSettingsProvider 的方式不同于其他工作区提供程序。 工厂不是 IWorkspaceProviderFactory,也没有特殊的属性类型。 请改为实现 IWorkspaceSettingsProviderFactory 并使用 [Export(typeof(IWorkspaceSettingsProviderFactory))]。
// Common workspace provider factory pattern
[ExportFeatureProvider(some, args, to, export)]
internal class MyProviderFactory : IWorkspaceProviderFactory<IFeatureProvider>
{
IFeatureProvider CreateProvider(IWorkspace workspace) => new Provider(workspace);
}
// IWorkspaceSettingsProvider pattern
[Export(typeof(IWorkspaceSettingsProviderFactory))]
internal class MySettingsProviderFactory : IWorkspaceSettingsProviderFactory
{
// 100 is typically the value used by built-in settings providers. Lower value is higher priority.
int Priority => 100;
IWorkspaceSettingsProvider CreateSettingsProvider(IWorkspace workspace) => new MySettingsProvider(workspace);
}
提示
实现返回 IWorkspaceSettingsSource(如 IWorkspaceSettingsProvider.GetSingleSettings)的方法时,返回 IWorkspaceSettings 实例,而不是 IWorkspaceSettingsSource。
IWorkspaceSettings 提供了可在聚合某些设置时使用的其他信息。
设置相关的 API
- IWorkspaceSettingsManager 读取和聚合工作区的设置。
-
GetSettingsManager 获取工作区的
IWorkspaceSettingsManager。 - GetAggregatedSettings 获取在所有重叠范围中聚合的给定范围设置。
- IWorkspaceSettings 包含特定范围的设置。
工作区最佳实践
- 从
IWorkspaceProviderFactory.CreateProvider或类似 API 返回可在创建时记住Workspace上下文的对象。 在编写提供程序接口时预计到了在创建时会保留此对象。 - 在工作区的“本地设置”路径中保存特定于工作区的缓存或设置。 使用 Visual Studio 2017 版本 15.6 或更高版本中的
Microsoft.VisualStudio.Workspace.WorkspaceHelper.MakeRootedUnderWorkingFolder为文件创建路径。 对于版本 15.6 之前的版本,请使用以下代码片段:
using System.IO;
using Microsoft.VisualStudio.Workspace;
using Microsoft.VisualStudio.Workspace.Settings;
private static string MakeRootedUnderWorkingFolder(IWorkspace workspace, string relativePath)
{
string workingFolder = workspace.GetSettingsManager().GetAggregatedSettings(SettingsTypes.WorkspaceControlSettings).Property<string>("WorkingFolder");
return Path.Combine(workingFolder, relativePath);
}
解决方案事件和包自动加载
加载的包可以实现 IVsSolutionEvents7 并调用 IVsSolution.AdviseSolutionEvents。 其中包括在 Visual Studio 中打开和关闭文件夹时发生的事件。
UI 上下文可用于自动加载软件包。 该值为 4646B819-1AE0-4E79-97F4-8A8176FDD664。
故障 排除
SourceExplorerPackage 包未正确加载
工作区扩展性严重基于 MEF,组合错误将导致承载 Open Folder 的包无法加载。 例如,如果扩展导出具有 ExportFileContextProviderAttribute的类型,但该类型仅实现 IWorkspaceProviderFactory<IFileContextActionProvider>,则尝试在 Visual Studio 中打开文件夹时会发生错误。
可以在 %LOCALAPPDATA%\Microsoft\VisualStudio\16.0_id\ComponentModelCache\Microsoft.VisualStudio.Default.err中找到错误详细信息。 请解决由您的扩展实现的类型中的所有错误。