You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:31:00