将OptionsProvider移至类库后VSIX项目编译失败:类型加载异常
我正在开发一款管理GitHub Issues的Visual Studio扩展,受CommunityToolkit.MVVM特性限制,按照@Sergio0694的建议,我将ViewModel、服务等代码拆分到独立的VisGit.Core类库项目中,负责VS「工具>选项」的OptionsProvider也因依赖配置移至该类库。
主VSIX项目VisGit包含MainView工具窗口视图,通过XAML直接绑定VisGit.Core中的MainViewModel实例,无代码后置逻辑:
<UserControl x:Class="VisGit.Views.MainView" xmlns:viewmodels="clr-namespace:VisGit.Core.ViewModels;assembly=VisGit.Core"/> <UserControl.DataContext> <viewmodels:MainViewModel /> </UserControl.DataContext>
VisGit项目已引用VisGit.Core,VisGit.Core已引用Community.VisualStudio.Toolkit.17。我在VisGit.Core中编写的OptionsProvider代码如下:
namespace VisGit.Core.Services { public partial class OptionsProvider { // Register the options with this attribute on your package class: // [ProvideOptionPage(typeof(OptionsProvider.UserSettingsOptions), "VisGit.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)] [ComVisible(true)] public class UserSettingsOptions : BaseOptionPage<UserSettings> { } } public class UserSettings : BaseOptionModel<UserSettings> { private string personalAccessToken = "{UNSET}"; [DisplayName("Personal Access Token")] public string PersonalAccessToken { get => Encyption.DpapiToInsecureString(Encyption.DpapiDecryptString(personalAccessToken)); set => personalAccessToken = Encyption.DpapiEncryptString(Encyption.DpapiToSecureString(value)); } } }
随后在VisGit项目的VisGitPackage.cs中配置ProvideOptionPage属性,指向类库中的UserSettingsOptions:
namespace VisGit { [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)] [ProvideToolWindow(typeof(MainToolWindow.Pane), Style = VsDockStyle.Tabbed, Window = WindowGuids.SolutionExplorer)] [ProvideMenuResource("Menus.ctmenu", 1)] [Guid(PackageGuids.VisGitString)] [ProvideOptionPage(typeof(VisGit.Core.Services.OptionsProvider.UserSettingsOptions), "VisGit.Core.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)] public sealed class VisGitPackage : ToolkitPackage { protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress) { await this.RegisterCommandsAsync(); this.RegisterToolWindows(); } } }
目前VisGit.Core可正常编译,但VisGit项目编译失败,报错如下:
Error CreatePkgDef : error : TypeLoadException: Could not load type 'VisGit.Core.Services.OptionsProvider+UserSettingsOptions' from assembly 'VisGit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. at System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs) at System.Reflection.CustomAttribute.CreateCaObject(RuntimeModule module, IRuntimeMethodInfo ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs) at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent) at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit) at Microsoft.VisualStudio.Tools.CreatePkgDef.ProcessAssembly(String fileName, Hive hive, PkgDefContext context, Boolean register, RegistrationMode mode) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 341 at Microsoft.VisualStudio.Tools.CreatePkgDef.DoCreatePkgDef(InputArguments inputArguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 202 at Microsoft.VisualStudio.Tools.CreatePkgDef.Main(String[] arguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 91 VisGit
注意到OptionsProvider是partial类,但无其他分部定义。当前架构下,ViewModel通过XAML实例化,无法在VSIX项目中实例化UserSettings并传入ViewModel,且希望ViewModel能独立获取配置,寻求该编译异常的解决方法。
1. 修复Partial类定义问题
OptionsProvider标记为partial但无其他分部实现,会导致类型加载异常。直接移除partial修饰符,改为普通类:
public class OptionsProvider { [ComVisible(true)] public class UserSettingsOptions : BaseOptionPage<UserSettings> { } }
2. 确保类库引用的正确性
- 检查
VisGit项目对VisGit.Core的引用为项目引用,而非文件引用,避免编译时无法定位最新程序集。 - 统一两个项目的输出目录,在项目属性「生成」选项中设置相同的输出路径,确保依赖程序集可被正确找到。
3. 调整OptionsProvider的可见性与COM注册
- 确认
VisGit.Core项目「生成」属性中勾选了「使程序集COM可见」,或手动在AssemblyInfo.cs添加[assembly: ComVisible(true)],保证UserSettingsOptions的[ComVisible(true)]属性生效。 - 简化
OptionsProvider的命名空间,例如改为VisGit.Core.Options,避免嵌套过深导致类型解析失败。
4. 实现ViewModel独立获取配置的方案
由于ViewModel通过XAML实例化无法直接注入配置,可在VisGit.Core中添加静态配置服务,借助Toolkit API获取配置:
namespace VisGit.Core.Services { public static class ConfigurationService { public static UserSettings GetUserSettings() { return VS.Services.GetOptionsAsync<UserSettings>().GetAwaiter().GetResult(); } } }
在MainViewModel中调用该服务:
public class MainViewModel : ObservableObject { private readonly UserSettings _settings; public MainViewModel() { _settings = ConfigurationService.GetUserSettings(); // 后续直接使用_settings中的配置项 } }
5. 清理缓存并重新生成
- 删除解决方案下所有
bin和obj目录,清除编译缓存。 - 先编译
VisGit.Core项目,再编译VisGit项目,确保依赖程序集为最新版本。
内容的提问来源于stack exchange,提问作者stigzler

