如何用ComWrappers源生成重写Windows App SDK WidgetProvider注册代码?
Windows App SDK Widget AOT兼容的COM互操作改写方案
问题核心原因
启用<PublishAot>true</PublishAot>后,传统手动编写的COM互操作代码(如直接实现类工厂、调用CoRegisterClassObject)会因.NET AOT的裁剪机制和COM互操作限制,触发警告并抛出System.NotSupportedException。这类代码依赖.NET运行时的动态COM支持,而AOT会移除未显式声明的类型和成员,导致兼容性问题。
基于ComWrappers的改写步骤
1. 显式声明WidgetProvider的COM接口
将官方要求的COM接口用.NET特性标记显式声明,确保AOT能识别类型信息:
[ComImport] [Guid("你的WidgetProvider接口GUID")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IWidgetProvider { // 按官方文档声明WidgetProvider必需方法 void Initialize(IntPtr widgetManager); void CreateWidget([MarshalAs(UnmanagedType.LPWStr)] string widgetId, IntPtr widgetContext); // 补充其他要求的方法 }
2. 实现自定义ComWrappers子类
创建ComWrappers子类,负责管理.NET对象与COM对象的映射,替代原有的手动COM注册逻辑:
public class WidgetComWrappers : ComWrappers { private static readonly Guid WidgetProviderClassId = new Guid("你的WidgetProvider类GUID"); protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) { if (obj is IWidgetProvider provider) { count = 1; var entries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(WidgetComWrappers), sizeof(ComInterfaceEntry) * count); entries[0].IID = typeof(IWidgetProvider).GUID; entries[0].Vtable = (IntPtr)ComputeVtable<IWidgetProvider>(flags); return entries; } count = 0; return null; } protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) { // Widget场景无需从COM对象创建.NET对象,直接抛出异常 throw new NotImplementedException(); } protected override void ReleaseObjects(IEnumerable<object> objects) { foreach (var obj in objects) { if (obj is IDisposable disposable) disposable.Dispose(); } } // 封装类对象注册逻辑 public unsafe void RegisterWidgetProviderClass<TProvider>() where TProvider : IWidgetProvider, new() { var classFactory = new WidgetClassFactory<TProvider>(); var classFactoryPtr = GetOrCreateComInterfaceForObject(classFactory, CreateComInterfaceFlags.None); int result = CoRegisterClassObject( ref WidgetProviderClassId, classFactoryPtr, CLSCTX.CLSCTX_LOCAL_SERVER, REGCLS.REGCLS_MULTIPLEUSE, out uint cookie); if (result != 0) Marshal.ThrowExceptionForHR(result); } // P/Invoke声明CoRegisterClassObject [DllImport("ole32.dll")] private static extern unsafe int CoRegisterClassObject( ref Guid rclsid, IntPtr pUnk, CLSCTX dwClsContext, REGCLS flags, out uint lpdwRegister); } // 辅助枚举定义 public enum CLSCTX : uint { CLSCTX_LOCAL_SERVER = 0x4 } public enum REGCLS : uint { REGCLS_MULTIPLEUSE = 0x1 }
3. 实现符合规范的类工厂
实现IClassFactory接口,通过ComWrappers暴露给COM系统:
[ComImport] [Guid("00000001-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IClassFactory { void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject); void LockServer(bool fLock); } public class WidgetClassFactory<TProvider> : IClassFactory where TProvider : IWidgetProvider, new() { public void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject) { if (pUnkOuter != IntPtr.Zero) Marshal.ThrowExceptionForHR(unchecked((int)0x80004002)); // E_NOINTERFACE var provider = new TProvider(); ppvObject = ComWrappers.GetOrCreateComWrapperForObject(provider, CreateComInterfaceFlags.None); } public void LockServer(bool fLock) { // Widget场景无需实现服务器锁定逻辑,留空即可 } }
4. 应用启动时初始化注册
在应用入口(如OnLaunched方法)添加初始化代码,完成ComWrappers注册和WidgetProvider类对象注册:
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { // 初始化并注册ComWrappers var widgetComWrappers = new WidgetComWrappers(); ComWrappers.RegisterForTrackerSupport(widgetComWrappers); // 注册自定义WidgetProvider实现 widgetComWrappers.RegisterWidgetProviderClass<YourWidgetProviderImpl>(); // 其他应用启动逻辑... }
关键注意事项
- 所有COM接口必须用
[ComImport]、[Guid]、[InterfaceType]显式标记,确保AOT能识别类型元数据。 IClassFactory的方法顺序必须严格遵循IUnknown接口规范,不能调整顺序。- 确保项目目标框架为.NET 6及以上版本,ComWrappers在该版本后才提供稳定支持。
内容的提问来源于stack exchange,提问作者Generic
相关产品推荐
相关产品推荐

