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

 

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

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


本文分为两个部分:

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

本文将一个普通项目改造成 UWP 项目,重点在了解 UWP 的项目文件组成。

 

从最简单的项目模板开始

虽然可以从零开始写一个 csproj 文件,不过这并没有什么技术含量,因为新的 csproj 文件实在是非常简单。参见:

  • 理解 C# 项目 csproj 文件格式的本质和编译流程
  • 将 WPF、UWP 以及其他各种类型的旧 csproj 迁移成基于 Microsoft.NET.Sdk 的新 csproj

于是,我创建一个 .NET Core 控制台应用。当然,其它简单的如 .NET Standard 库都是一样的,反正最后都会被我改得面目全非。

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

于是我得到了一个 csproj 项目文件和包含有应用程序入口的 Program.cs 文件。

两个文件

其中 csproj 文件内容非常简单:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>ExeOutputType>
    <TargetFramework>netcoreapp2.1TargetFramework>
  PropertyGroup>
Project>

Program.cs 文件也是:

// using System;

namespace Walterlv.Demo.ZeroUwp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 这一句需要删除,因为 UWP 程序中不能使用控制台。
            // Console.WriteLine("Hello World!");
        }
    }
}

不过,这两个文件都会被改掉的,已经无所谓里面是什么内容了。

将项目改造成 UWP 项目

UWP 程序的输出类型是 AppContainerExe,而不是一般的 Library 或者 Exe。

另外,基于 Microsoft.NET.Sdk 的新 csproj 格式不支持 UWP 应用程序。所以我希望借助第三方的 MSBuild.Sdk.Extras 来编译 UWP 的项目。参见 新 csproj 对 WPF/UWP 支持不太好?有第三方 SDK 可以用!MSBuild.Sdk.Extras。

然而实际情况也不容乐观,因为此第三方 Sdk 只支持 UWP 的库程序,而不支持应用程序容器。所以即便修改为以下方式,最终也因为缺少 Visual Studio RunCommand 的支持,而导致无法启动。

<Project Sdk="MSBuild.Sdk.Extras/1.6.41">

  <PropertyGroup>
    <OutputType>AppContainerExeOutputType>
    <TargetFrameworks>uap10.0.17134TargetFrameworks>
  PropertyGroup>

Project>

使用以上新 Sdk 的 csproj 格式,我完整地写完了整个 csproj 文件和后续步骤,依然无法解决下面这个错误提示框:

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第2张图片
▲ 无法启动

所以我们依然只能使用传统的 csproj 文件格式。里面大部分的内容从模板中复制而来。事实上,我寻找了很多资料,都没有找到让支持 Sdk 的新 csproj 格式支持 UWP 的主程序。


<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">DebugConfiguration>
    <Platform Condition=" '$(Platform)' == '' ">x86Platform>
    <ProjectGuid>{09A58639-DC50-41C1-8BCE-A2217A1D3327}ProjectGuid>
    <OutputType>AppContainerExeOutputType>
    <AppDesignerFolder>PropertiesAppDesignerFolder>
    <RootNamespace>Walterlv.Demo.ZeroUwpRootNamespace>
    <AssemblyName>Walterlv.Demo.ZeroUwpAssemblyName>
    <DefaultLanguage>en-USDefaultLanguage>
    <TargetPlatformIdentifier>UAPTargetPlatformIdentifier>
    <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17134.0TargetPlatformVersion>
    <TargetPlatformMinVersion>10.0.15063.0TargetPlatformMinVersion>
    <MinimumVisualStudioVersion>14MinimumVisualStudioVersion>
    <FileAlignment>512FileAlignment>
    <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}ProjectTypeGuids>
    <WindowsXamlEnableOverview>trueWindowsXamlEnableOverview>
    <DebugSymbols>trueDebugSymbols>
    <OutputPath>bin\x86\Debug\OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <DebugType>fullDebugType>
    <PlatformTarget>x86PlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
  PropertyGroup>
  <PropertyGroup>
    <RestoreProjectStyle>PackageReferenceRestoreProjectStyle>
  PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  ItemGroup>
  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>DesignerSubType>
    AppxManifest>
  ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
      <Version>6.1.5Version>
    PackageReference>
  ItemGroup>
  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
    <VisualStudioVersion>14.0VisualStudioVersion>
  PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
Project>

编写 AppxManifest

项目改造成 UWP 项目后,似乎已经完成了大部分了,但此时直接运行会有编译错误,因为我缺少 UWP 程序必要的 AppxManifest.xml 文件。

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第3张图片

事实上,AppxManifest.xml 的创建是非常繁琐的;通常是编译过程帮我们根据 Package.appxmanifest 文件自动生成的。然而创建一个 Package.appxmanifest 也是很麻烦的。至少,要让 Visual Studio 能够直接打开这个文件所需的最小代码量是下面这些(不能编译通过):


<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
         xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
         xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
         xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
         xmlns:mobile="http://schemas.microsoft.com/appx/manifest/mobile/windows10"
         IgnorableNamespaces="uap mp uap3 iot uap2 mobile">
  <Identity Name="walterlv.zerouwp" Publisher="CN=walterlv" Version="0.1.0.0" />
  <mp:PhoneIdentity PhoneProductId="97f5137d-c6be-4395-9af0-bbfdcea40fa7" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
  <Properties>
    <DisplayName>Walterlv.ZeroUwpDisplayName>
    <PublisherDisplayName>walterlvPublisherDisplayName>
    <Logo>Assets\StoreLogo.pngLogo>
  Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
  Dependencies>
  <Resources>
    <Resource Language="x-generate" />
  Resources>
  <Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Walterlv.ZeroUwp.Program">
      <uap:VisualElements DisplayName="Walterlv.ZeroUwp">
      uap:VisualElements>
    Application>
  Applications>
Package>

可以阅读这些文档了解如何完成这份文件的编写:

  • Identity (Windows 10) - UWP app developer - Microsoft Docs
  • pm:PhoneIdentity (Windows 10) - UWP app developer - Microsoft Docs

具体来说, 是此程序包的标识符,需要在整个应用商店范围内唯一(如果将此包与应用商店关联,这个值会自动更新,所以不用在意填成什么)。 是此程序包在移动设备上的标识符,应用的更新会依据此标识符的 GUID 来唯一确定,格式必须是 GUID。

事实上,虽然依然无法完成编译,但此时可以通过在 Visual Studio 中打开这份文件来观察还缺少哪些必要的信息需要填写。

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第4张图片

事实上,我们缺少的信息并不多,只有四个,都从 Package/Applications/Application 开始:

  • uap:VisualElements@Description
  • uap:VisualElements@BackgroundColor
  • uap:VisualElements@Square150x150Logo
  • uap:VisualElements@Square44x44Logo
  • uap:VisualElements/uap:DefaultTile@Wide310x150Logo

这是 XPath 语法,详见:XML 的 XPath 语法

同时,我们还真的需要相应的图片:

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第5张图片

建议从 UWP 程序模板中复制,也可以去这里下载:UWP 程序所需的最少 Logo 资源-CSDN下载。

补充完毕之后,完整的文件如下:


<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
         xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
         xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
         xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
         xmlns:mobile="http://schemas.microsoft.com/appx/manifest/mobile/windows10"
         IgnorableNamespaces="uap mp uap3 iot uap2 mobile">
  <Identity Name="walterlv.zerouwp" Publisher="CN=walterlv" Version="0.1.0.0" />
  <Properties>
    <DisplayName>Walterlv.ZeroUwpDisplayName>
    <PublisherDisplayName>walterlvPublisherDisplayName>
    <Logo>Assets\StoreLogo.pngLogo>
  Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
  Dependencies>
  <Resources>
    <Resource Language="x-generate" />
  Resources>
  <Applications>
    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Walterlv.ZeroUwp.Program">
      <uap:VisualElements DisplayName="Walterlv.ZeroUwp" Description="Walterlv.ZeroUwp is a demo application to learn how uwp application runs." BackgroundColor="transparent" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png">
        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
        uap:DefaultTile>
      uap:VisualElements>
    Application>
  Applications>
Package>

不能忘掉,这份文件还需要添加到 csproj 项目文件中:


  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>DesignerSubType>
    AppxManifest>
    <Content Include="Assets\Square150x150Logo.scale-200.png" />
    <Content Include="Assets\Square44x44Logo.scale-200.png" />
    <Content Include="Assets\StoreLogo.png" />
    <Content Include="Assets\Wide310x150Logo.scale-200.png" />
  ItemGroup>

编写 AssemblyInfo.cs

由于没有新的基于 Sdk 的 csproj 文件支持,所以我们需要自己编写 AssemblyInfo.cs 文件,并放入到 Properties 文件夹中。

using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("Walterlv.Demo.ZeroUwp")]
[assembly: AssemblyProduct("Walterlv.Demo.ZeroUwp")]
[assembly: AssemblyCopyright("Copyright © walterlv 2018")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: ComVisible(false)]

最后,csproj 文件会如下面这样。


<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">DebugConfiguration>
    <Platform Condition=" '$(Platform)' == '' ">x86Platform>
    <ProjectGuid>{7B81D14B-6094-44E1-9B2F-F577995A3CAF}ProjectGuid>
    <OutputType>AppContainerExeOutputType>
    <AppDesignerFolder>PropertiesAppDesignerFolder>
    <RootNamespace>Walterlv.Demo.ZeroUwpRootNamespace>
    <AssemblyName>Walterlv.Demo.ZeroUwpAssemblyName>
    <DefaultLanguage>en-USDefaultLanguage>
    <TargetPlatformIdentifier>UAPTargetPlatformIdentifier>
    <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17134.0TargetPlatformVersion>
    <TargetPlatformMinVersion>10.0.17134.0TargetPlatformMinVersion>
    <MinimumVisualStudioVersion>14MinimumVisualStudioVersion>
    <FileAlignment>512FileAlignment>
    <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}ProjectTypeGuids>
    <WindowsXamlEnableOverview>trueWindowsXamlEnableOverview>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
    <DebugSymbols>trueDebugSymbols>
    <OutputPath>bin\x86\Debug\OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <NoWarn>;2008NoWarn>
    <DebugType>fullDebugType>
    <PlatformTarget>x86PlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
    <OutputPath>bin\x86\Release\OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <Optimize>trueOptimize>
    <NoWarn>;2008NoWarn>
    <DebugType>pdbonlyDebugType>
    <PlatformTarget>x86PlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
    <UseDotNetNativeToolchain>trueUseDotNetNativeToolchain>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
    <DebugSymbols>trueDebugSymbols>
    <OutputPath>bin\ARM\Debug\OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <NoWarn>;2008NoWarn>
    <DebugType>fullDebugType>
    <PlatformTarget>ARMPlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
    <OutputPath>bin\ARM\Release\OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <Optimize>trueOptimize>
    <NoWarn>;2008NoWarn>
    <DebugType>pdbonlyDebugType>
    <PlatformTarget>ARMPlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
    <UseDotNetNativeToolchain>trueUseDotNetNativeToolchain>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>trueDebugSymbols>
    <OutputPath>bin\x64\Debug\OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <NoWarn>;2008NoWarn>
    <DebugType>fullDebugType>
    <PlatformTarget>x64PlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
  PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWPDefineConstants>
    <Optimize>trueOptimize>
    <NoWarn>;2008NoWarn>
    <DebugType>pdbonlyDebugType>
    <PlatformTarget>x64PlatformTarget>
    <UseVSHostingProcess>falseUseVSHostingProcess>
    <ErrorReport>promptErrorReport>
    <Prefer32Bit>truePrefer32Bit>
    <UseDotNetNativeToolchain>trueUseDotNetNativeToolchain>
  PropertyGroup>
  <PropertyGroup>
    <RestoreProjectStyle>PackageReferenceRestoreProjectStyle>
  PropertyGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Program.cs" />
    <Compile Include="VisualProperties.cs" />
  ItemGroup>
  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>DesignerSubType>
    AppxManifest>
    <Content Include="Assets\Square150x150Logo.scale-200.png" />
    <Content Include="Assets\Square44x44Logo.scale-200.png" />
    <Content Include="Assets\StoreLogo.png" />
    <Content Include="Assets\Wide310x150Logo.scale-200.png" />
  ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
      <Version>6.1.5Version>
    PackageReference>
  ItemGroup>
  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
    <VisualStudioVersion>14.0VisualStudioVersion>
  PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
Project>

一个说明:如果运行时出现本机错误,那么可能是上面的 csproj 文件没有配置正确。如果出现下图所示的错误,建议先考虑将以上 csproj 文件中的所有内容复制到你的项目文件中再试。

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第6张图片

完成部署和运行

以上所有内容是一个 UWP 程序完成编译并运行所需的最少信息了。

此时运行,我们只会看到一个空的窗口,就像这样:

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第7张图片

Main 函数中的断点是可以进入的:

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第8张图片

不过,如果继续运行,会提示错误。因为我们的程序并没有显示任何 UWP 的界面。

(1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序_第9张图片

总结与后续

在本文中,我们了解到 UWP 项目所需的最少文件有:

  • *.csproj 项目文件
    • 这是整个从零开始的 UWP 程序中最复杂的一个文件,因为目前没有找到任何一个 Sdk 支持 UWP 的主程序工程。
  • Package.appxmanifest 文件
    • 这是 UWP 应用程序的清单文件。事实上,这不是最终的清单文件,而是用于在项目中填写信息的文件;从前面的错误信息中我们了解到,最终的清单文件是 AppxManifest.xml。
  • Assets 文件夹中的四张图片
    • StoreLogo、Square44x44Logo、Square150x150Logo 和 Wide310x150Logo 是清单文件能够正常生成所需的最少 Logo 资源
  • AssemblyInfo.cs
    • 由于缺少 Project@Sdk 的支持,所以我们必须编写 AssemblyInfo.cs 文件来指定版本信息。
  • Program.cs
    • 这是一开始我们就添加好的文件,就是放 Main 函数的地方。虽然我们什么都没写,但已经能够进入断点了。

接下来我们将从 Main 函数开始,完成一个 UWP 程序的启动:(2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序。

 

转载于:https://www.cnblogs.com/walterlv/p/10236436.html

你可能感兴趣的:((1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序)