每次使用 Visual Studio 的模板创建一个 UWP 程序,我们会在项目中发现大量的项目文件、配置、应用启动流程代码和界面代码。然而这些文件在 UWP 程序中到底是如何工作起来的?

我从零开始创建了一个 UWP 程序,用于探索这些文件的用途,了解 UWP 程序的启动流程。


本文分为两个部分:

  • 从零开始创建一个 UWP 项目并完成部署
  • 从零开始编写一个 UWP 应用程序和窗口

本文将从 Main 函数开始,一步步跑起来一个应用程序,显示一个窗口,并在窗口中显示一些内容。重点在了解在 UWP 中运行应用程序,并显示窗口。

启动应用

在上一篇文章中的末尾,我们成功启动了程序并进入了 Main 函数的断点,但实际上运行会报错。我们能看见一个窗口显示出来,随后提示进程已启动,但应用尚未运行。

The Walterlv.Demo.ZeroUwp.exe process started, but the activation request failed with error ‘The app didn’t start’.

是的,我们只有一个什么都没做的 Main 函数,进程当然能够成功启动;但我们需要能够启动应用。那么 UWP 的应用是什么呢?是 CoreApplication。

所以我们使用 CoreApplication 类型执行 Run 静态方法。

此方法要求传入一个 IFrameworkViewSource。事实上 UWP 已经有一个 IFrameworkViewSource 的实现了,是 FrameworkViewSource。不过,我希望自己写一个,了解其原理。

所以,就用 ReSharper 生成了 IFrameworkViewSource 的一个实现:

using Windows.ApplicationModel.Core;namespace Walterlv.Demo.ZeroUwp
{internal sealed class WalterlvViewSource : IFrameworkViewSource{public IFrameworkView CreateView() => new WalterlvFrameworkView();}
}

IFrameworkViewSource 接口中只有一个方法 CreateView,返回一个新的 IFrameworkView 的实例。

只是写一个 NotImplementedException 的异常,当然是跑不起来的,得返回一个真的 IFrameworkView 的实例。UWP 自带的实现为 FrameworkView,那么我也自己实现一个。

这次需要实现的方法会多一些:

using Windows.ApplicationModel.Core;
using Windows.UI.Core;namespace Walterlv.Demo.ZeroUwp
{internal sealed class WalterlvFrameworkView : IFrameworkView{public void Initialize(CoreApplicationView applicationView) => throw new System.NotImplementedException();public void SetWindow(CoreWindow window) => throw new System.NotImplementedException();public void Load(string entryPoint) => throw new System.NotImplementedException();public void Run() => throw new System.NotImplementedException();public void Uninitialize() => throw new System.NotImplementedException();}
}

因此,我们需要理解这些方法的执行时机以及含义才能正确实现这些方法。庆幸的是,这些方法的含义都能在官方文档中找到(其实就是平时看到的注释):

  • IFrameworkView.Initialize(CoreApplicationView)
  • IFrameworkView.Load(String)
  • IFrameworkView.Run
  • IFrameworkView.SetWindow(CoreWindow)
  • IFrameworkView.Uninitialize

为了方便查看,我将其整理到这些方法上作为注释。

顺便的,下面这些方法刚好是按照应用生命周期的顺序被调用,也就是 Initialize->SetWindow->Load->Run->Uninitialize

/// <summary>
/// 当应用启动时将执行此方法。进行必要的初始化。
/// </summary>
public void Initialize(CoreApplicationView applicationView) { }/// <summary>
/// 每次应用需要显示一个窗口的时候,此方法就会被调用。用于为当前应用程序显示一个新的窗口视图。
/// </summary>
public void SetWindow(CoreWindow window) { }/// <summary>
/// 会在 <see cref="Run"/> 方法执行之前执行。如果需要使用外部资源,那么这时需要将其加载或激活。
/// </summary>
public void Load(string entryPoint) { }/// <summary>
/// 当此方法调用时,需要让应用内的视图(View)显示出来。
/// </summary>
public void Run() { }/// <summary>
/// 当应用退出时将执行此方法。如果应用启动期间使用到了外部资源,需要在此时进行释放。
/// </summary>
public void Uninitialize() { }

在此接口的所有方法留空地实现完以后,我们的 UWP 应用终于能跑起来了。当按下 F5 调试之后,不会再提示错误,而是依次执行这五个方法后,正常退出应用。

启动窗口

注意到以上所有方法都留空之后,应用程序很快就退出了。这与我们开发传统 Win32 应用时的效果是一致的 —— 是的,我们缺一个消息循环。我们需要一个不断处理的消息循环用来阻断主线程的退出,同时又能够不断响应消息。而这样的方法需要写到 Run() 方法里面。

UWP 中开启一个消息循环是非常容易的,不过我们需要一个 CoreDispatcher 对象。在我们目前的接口实现中,CoreDispatcher 对象可以从 CoreWindow 中获取到。所以我们需要在 SetWindow 方法中拿到 CoreWindow 的实例,然后在 Run 中使用它开启窗口消息循环。

public void SetWindow(CoreWindow window)
{_window = window;
}public void Run()
{_window.Activate();_window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
}private CoreWindow _window;


▲ 开启了消息循环之后,应用不会直接退出了

在窗口中显示点东西

我们使用 CompositionAPI 可以在窗口中创建 Visual 并显示出来。

public void SetWindow(CoreWindow window)
{_window = window;var compositor = new Compositor();var root = compositor.CreateContainerVisual();var compositionTarget = compositor.CreateTargetForCurrentView();compositionTarget.Root = root;var child = compositor.CreateSpriteVisual();child.Size = new Vector2(100f, 100f);child.Brush = compositor.CreateColorBrush(Color.FromArgb(0xFF, 0x00, 0x80, 0xFF));root.Children.InsertAtTop(child);
}

在窗口中做一些交互

CoreWindow 除了为我们提供了消息循环之外,也可以提供交互。监听 PointerMoved 事件,我们可以做一些简单的交互。

下面我用 Git 标准差异比较的方式添加了交互的代码 PointerMoved

  public void SetWindow(CoreWindow window){_window = window;
+     _window.PointerMoved += OnPointerMoved;var compositor = new Compositor();
-     var root = compositor.CreateContainerVisual();
+     _root = compositor.CreateContainerVisual();var compositionTarget = compositor.CreateTargetForCurrentView();
-     compositionTarget.Root = _root;
+     compositionTarget.Root = _root;var child = compositor.CreateSpriteVisual();child.Size = new Vector2(100f, 100f);child.Brush = compositor.CreateColorBrush(Color.FromArgb(0xFF, 0x00, 0x80, 0xFF));
-     root.Children.InsertAtTop(child);
+     _root.Children.InsertAtTop(child);}+ private void OnPointerMoved(CoreWindow sender, PointerEventArgs args)
+ {
+     var visual = _root.Children.First();
+     var position = args.CurrentPoint.Position;
+     visual.Offset = new Vector3((float) (position.X - 50f), (float) (position.Y - 50f), 0f);
+ }private CoreWindow _window;
+ private ContainerVisual _root;

能够完成一些简单的交互。

总结

在本文中,我们了解到 UWP 的应用程序启动中也一样需要有窗口消息循环。不过 UWP 中创建消息循环还是非常简单的。

我们使用 CompositionAPI 进行了一些界面显示和简单的交互。了解到即便是如此复杂的 UWP 程序,其启动流程也没有那么复杂。

不过,如果你阅读了前面一篇 (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序,会发现复杂的部分都在项目文件和系统的部分。

(2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序相关推荐

  1. (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序

    (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 原文:(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序 版权声明:本作品采用知识共享署名-非商 ...

  2. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 最详细最易理解的linux启动流程解读及相关问题,万字高能无废话

    linux启动流程 linux的启动流程是运维所必须非常理解的东西,因为它将会是我们在发生服务器开不起机等情况的主要排错手段,充分理解了liunx的启动流程能够助力我们对此类故障的快速定位和处理. 废 ...

  4. Alian解读SpringBoot 2.6.0 源码(六):启动流程分析之创建应用上下文

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.创建应用上下文 2.1.初始化入口 2.2.初始化AbstractApplicationContext 2.3.初始化Generi ...

  5. SpringBoot(十二)启动流程分析之创建应用上下文AnnotationConfigServletWebServerApplicationContext

    SpringBoot版本:2.1.1      ==>启动流程分析汇总 接上篇博客Spring Boot 2.1.1(十一)启动流程分析之设置系统属性spring.beaninfo.ignore ...

  6. Android手机启动流程与TEE OS

    2019独角兽企业重金招聘Python工程师标准>>> 转载:https://cloud.tencent.com/developer/article/1043659 一个移植了TEE ...

  7. 【嵌入式开发】ARM 代码搬移 ( ARM 启动流程 | 代码搬移 起点 终点 | 链接地址 | 汇编代码 )

    文章目录 一. ARM 启动流程 1. 各种类型开发板启动流程 ( 1 ) 2440 开发板启动流程简介 ( ① Nand Flash 拷贝 4 KB -> SRAM 垫脚石 | ② PC 指向 ...

  8. 深入分析Android 9.0源代码——Service启动流程(startService方式)

    引言 点击此处查看<深入分析Android 9.0源代码>系列的组织结构和相关说明. 1 应用进程发起启动请求 本章的调用流程如下图所示: (Context)ContextWrapperC ...

  9. 一图看懂RISC-V星光板(VisionFive)的启动流程

    继<在RISC-V星光板上创建Debian系统镜像>之后,这一期来聊聊RISC-V星光板的启动流程. 如何更直观理解VisionFive的启动流程呢?小编用一张图摹拟整个过程. 通电开机加 ...

最新文章

  1. Android自定义View —— TypedArray
  2. IAB303 Data Analytics Assessment Task
  3. centos7安装mariadb
  4. 如何在一个领域内成为顶尖人才?
  5. [转]open channel SSD FTL
  6. 九价抢不到?多试试这几个GitHub上的开源项目
  7. OSChina 周一乱弹 —— 深入浅出讲技术
  8. DM8168 编译filesystem步骤
  9. 什么是SVC模式【转】
  10. JAVA格式化当前日期或者取年月日
  11. rabbitmq配置文件字段spring.rabbitmq.publisher-confirms过时
  12. matlab各类数学公式
  13. 好教程推荐系列:《计算机视觉--算法与应用》和《机器视觉算法与应用》等等
  14. 原生JavaScript开发高级课程 |智能S
  15. 上传本地项目到码云仓库【图文详解】
  16. python两个表格相同数据筛选的方法_浅谈pandas筛选出表中满足另一个表所有条件的数据方法...
  17. 第一天:2个法则,你的第一桶金可以这么来
  18. .NET 结构体 Struck、类
  19. Podman容器 [2022]
  20. 重保防护 全力以赴丨一文看懂盛邦安全重保专项服务方案

热门文章

  1. 《十年》中的没有颤抖的那两个字——“始于你好,终于你好”
  2. 身份管理的15个安全开发实践
  3. CoreXY运动结构工作原理
  4. OpenAPI 标准规范,了解一下?
  5. 安卓调用手机自带的浏览器
  6. PYTHON实现自动发送邮件(QQ,163,139三种邮箱演示)
  7. Go语言基础数据类型所占内存大小
  8. 自己动手实现远程执行功能
  9. html表单设计姓名性别,编写一个表单页面census.html,让用户填写姓名、性别(男女选择)、兴趣(运动,读书,音乐,书法及其他)...
  10. 【期末复习】技术经济学(南邮储成祥)