【UAP】利用 .NET Core App 编写 UAP

打印 上一主题 下一主题

主题 942|帖子 942|积分 2826

众所周知,2024年9月微软正式公布了 .NET Core App 的 UWP 支持,至此我们终于可以在新版 csproj 用 .NET 8 及以上编写 UWP 了,那么我们可不可以通过修改清单的方式来让 UWP 变成 UAP 呢?

UWP 和 UAP 利用的是同一套 WinRT API ,Windows 区分 UAP 和 UWP 的方式是看清单,只要是用 UAP 的清单就会仿真成 Win8.1 模式,于是我们只需要将清单变成 UAP 的样子就行了。所以首先我们新建一个 .NET 9 Native AOT 的 UWP 项目

然后我们修改清单,Win8 App 清单如下,内容按需填写,Win8.1 App 的清单可以通过在 Github 搜刮OSMaxVersionTested language:XML找到,6.2是 Win8,6.3是 Win8.1 ($targetentrypoint$需配合 ApplicationEntryPoint利用)
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Package xmlns="http://schemas.microsoft.com/appx/2010/manifest">
  3.   <Identity
  4.     Name="wherewhere.CoreAppUAP"
  5.     Publisher="CN=where"
  6.     Version="0.0.1.0" />
  7.   <Properties>
  8.     <DisplayName>CoreAppUAP</DisplayName>
  9.     <PublisherDisplayName>wherewhere</PublisherDisplayName>
  10.     <Logo>Assets\StoreLogo.png</Logo>
  11.   </Properties>
  12.   <Prerequisites>
  13.     <OSMinVersion>6.2.0</OSMinVersion>
  14.     <OSMaxVersionTested>6.3.0</OSMaxVersionTested>
  15.   </Prerequisites>
  16.   <Resources>
  17.     <Resource Language="x-generate"/>
  18.   </Resources>
  19.   <Applications>
  20.     <Application Id="App"
  21.       Executable="$targetnametoken$.exe"
  22.       EntryPoint="$targetentrypoint$">
  23.       <VisualElements
  24.         DisplayName="CoreAppUAP"
  25.         Logo="Assets\MediumTile.png"
  26.         SmallLogo="Assets\AppIcon.png"
  27.         Description="CoreAppUAP"
  28.         ForegroundText="light"
  29.         BackgroundColor="transparent">
  30.         <DefaultTile WideLogo="Assets\WideTile.png"/>
  31.         <SplashScreen Image="Assets\SplashScreen.png"/>
  32.         <InitialRotationPreference>
  33.           <Rotation Preference="landscape"/>
  34.           <Rotation Preference="portrait"/>
  35.           <Rotation Preference="landscapeFlipped"/>
  36.           <Rotation Preference="portraitFlipped"/>
  37.         </InitialRotationPreference>
  38.         <LockScreen Notification="badgeAndTileText" BadgeLogo="Assets\BadgeLogo.png"/>
  39.       </VisualElements>
  40.     </Application>
  41.   </Applications>
  42.   <Capabilities>
  43.     <Capability Name="internetClient" />
  44.   </Capabilities>
  45. </Package>
复制代码
然后我们在csproj中添加以下内容来取消引用VC++和TargetDeviceFamily
  1. <PropertyGroup>
  2.   <AddMicrosoftVCLibsSDKReference>False</AddMicrosoftVCLibsSDKReference>
  3.   <EnableAppxWindowsUniversalTargetDeviceFamilyItem>False</EnableAppxWindowsUniversalTargetDeviceFamilyItem>
  4. </PropertyGroup>
复制代码
不过删除了这些引用仍然会在清单生成Dependencies标签,如果Dependencies是空的注册时会报错,所以我们需要添加任务来删除清单中的Dependencies元素
  1. <UsingTask
  2.   TaskName="RemoveDependencies"
  3.   TaskFactory="CodeTaskFactory"
  4.   AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
  5.   <ParameterGroup>
  6.     <AppxManifestPath ParameterType="System.String" Required="true" />
  7.   </ParameterGroup>
  8.   <Task>
  9.     <Reference Include="System.Xml" />
  10.     <Reference Include="System.Xml.Linq" />
  11.     <Using Namespace="System.Linq" />
  12.     <Using Namespace="System.Xml.Linq" />
  13.    
  14.       <![CDATA[
  15.           try
  16.           {
  17.               var xdoc = XDocument.Load(AppxManifestPath);
  18.               var ns = xdoc.Root.Name.Namespace;
  19.               var dependencies = xdoc.Root.Descendants(ns + "Dependencies");
  20.               if (dependencies != null)
  21.               {
  22.                   foreach (var node in dependencies.ToArray())
  23.                   {
  24.                       if (!node.HasElements)
  25.                       {
  26.                           node.Remove();
  27.                       }
  28.                   }
  29.               }
  30.               xdoc.Save(AppxManifestPath);
  31.           }
  32.           catch
  33.           {
  34.               Log.LogError("Failed to load Appx Manifest.");
  35.               _Success = false;
  36.           }
  37.       ]]>
  38.     </Code>
  39.   </Task>
  40. </UsingTask>
  41. <Target
  42.   Name="RemoveDependencies"
  43.   AfterTargets="AfterGenerateAppxManifest">
  44.   <Message Importance="high" Text="RemoveDependencies" />
  45.   <RemoveDependencies AppxManifestPath="%(FinalAppxManifest.Identity)" />
  46. </Target>
复制代码
由于 XAML 编译器编译App.xaml时生成的入口点会利用DispatcherQueueSynchronizationContext来注册线程上下文,这是16299才参加的 API,UAP 在获取DispatcherQueue时会返回null,所以我们需要手动生成入口点和注册线程上下文
首先我们需要添加DISABLE_XAML_GENERATED_MAIN来注释自动生成的入口点
  1. <DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
复制代码
然后手动编写程序入口点
  1. public static class Program
  2. {
  3.     public static void Main(string[] args) => Application.Start(static p => _ = new App());
  4. }
复制代码
接着我们需要手动创建一个利用CoreDispatcher的线程上下文
  1. /// <summary>
  2. /// Provides a synchronization context for <see cref="CoreDispatcher"/>.
  3. /// </summary>
  4. /// <param name="dispatcher">The <see cref="CoreDispatcher"/> to associate this <see cref="CoreDispatcherSynchronizationContext"/> with.</param>
  5. public sealed class CoreDispatcherSynchronizationContext(CoreDispatcher dispatcher) : SynchronizationContext
  6. {
  7.     /// <inheritdoc />
  8.     public override void Post(SendOrPostCallback d, object? state)
  9.     {
  10.         ArgumentNullException.ThrowIfNull(d);
  11.         _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => d.Invoke(state));
  12.     }
  13.     /// <inheritdoc />
  14.     public override SynchronizationContext CreateCopy() => new CoreDispatcherSynchronizationContext(dispatcher);
  15.     /// <inheritdoc />
  16.     public override void Send(SendOrPostCallback d, object? state) => throw new NotSupportedException("'SynchronizationContext.Send' is not supported.");
  17. }
复制代码
并在合适的时间注册线程上下文,比如OnWindowCreated
  1. protected override void OnWindowCreated(WindowCreatedEventArgs e)
  2. {
  3.     if (SynchronizationContext.Current == null)
  4.     {
  5.         CoreDispatcherSynchronizationContext context = new(e.Window.Dispatcher);
  6.         SynchronizationContext.SetSynchronizationContext(context);
  7.     }
  8.     base.OnWindowCreated(e);
  9. }
复制代码
然后就可以点运行运行了

经测试,热重载等调试功能正常,UAP 可以调用一些与 UI 无关的新 WinRT API,乃至可以扩展标题栏,不过打包后无法成功在 Win8.1 安装,原因未知

末了附上示例应用:https://github.com/wherewhere/CoreAppUAP

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表