当前位置: 首页 > news >正文

(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 库都是一样的,反正最后都会被我改得面目全非。

创建 .NET Core 控制台应用

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

两个文件

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

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </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>AppContainerExe</OutputType>
    <TargetFrameworks>uap10.0.17134</TargetFrameworks>
  </PropertyGroup>

</Project>

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

RunCommand Property is not defined
▲ 无法启动

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

<?xml version="1.0" encoding="utf-8"?>
<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)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
    <ProjectGuid>{09A58639-DC50-41C1-8BCE-A2217A1D3327}</ProjectGuid>
    <OutputType>AppContainerExe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Walterlv.Demo.ZeroUwp</RootNamespace>
    <AssemblyName>Walterlv.Demo.ZeroUwp</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
    <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17134.0</TargetPlatformVersion>
    <TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion>
    <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x86\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x86</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>Designer</SubType>
    </AppxManifest>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
      <Version>6.1.5</Version>
    </PackageReference>
  </ItemGroup>
  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
    <VisualStudioVersion>14.0</VisualStudioVersion>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
</Project>

编写 AppxManifest

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

缺少 AppxManifest.xml 文件

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

<?xml version="1.0" encoding="utf-8"?>
<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.ZeroUwp</DisplayName>
    <PublisherDisplayName>walterlv</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </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

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

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

填写缺少的信息

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

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

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

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

UWP 程序所需的最少 Logo

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

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

<?xml version="1.0" encoding="utf-8"?>
<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.ZeroUwp</DisplayName>
    <PublisherDisplayName>walterlv</PublisherDisplayName>
    <Logo>Assets\StoreLogo.png</Logo>
  </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 项目文件中:

<!-- 新增了此节点,即 AppxManifest 和相关资源。 -->
  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>Designer</SubType>
    </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 文件会如下面这样。

<?xml version="1.0" encoding="utf-8"?>
<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)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
    <ProjectGuid>{7B81D14B-6094-44E1-9B2F-F577995A3CAF}</ProjectGuid>
    <OutputType>AppContainerExe</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Walterlv.Demo.ZeroUwp</RootNamespace>
    <AssemblyName>Walterlv.Demo.ZeroUwp</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
    <TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17134.0</TargetPlatformVersion>
    <TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
    <MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x86\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <NoWarn>;2008</NoWarn>
    <DebugType>full</DebugType>
    <PlatformTarget>x86</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
    <OutputPath>bin\x86\Release\</OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <Optimize>true</Optimize>
    <NoWarn>;2008</NoWarn>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x86</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\ARM\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <NoWarn>;2008</NoWarn>
    <DebugType>full</DebugType>
    <PlatformTarget>ARM</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
    <OutputPath>bin\ARM\Release\</OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <Optimize>true</Optimize>
    <NoWarn>;2008</NoWarn>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>ARM</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <NoWarn>;2008</NoWarn>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
    <Optimize>true</Optimize>
    <NoWarn>;2008</NoWarn>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <UseVSHostingProcess>false</UseVSHostingProcess>
    <ErrorReport>prompt</ErrorReport>
    <Prefer32Bit>true</Prefer32Bit>
    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
  </PropertyGroup>
  <PropertyGroup>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Program.cs" />
    <Compile Include="VisualProperties.cs" />
  </ItemGroup>
  <ItemGroup>
    <AppxManifest Include="Package.appxmanifest">
      <SubType>Designer</SubType>
    </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.5</Version>
    </PackageReference>
  </ItemGroup>
  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
    <VisualStudioVersion>14.0</VisualStudioVersion>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
</Project>

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

本机错误

完成部署和运行

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

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

空的窗口

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

Main 函数中的断点

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

无法继续运行

总结与后续

在本文中,我们了解到 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 程序。

相关文章:

  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • .NET/C# 使窗口永不获得焦点
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • WPF 应用完全模拟 UWP 的标题栏按钮
  • 让控制台支持 ANSI 转义序列,输出下划线、修改颜色或其他控制
  • 在 GitHub 公开仓库中隐藏自己的私人邮箱地址
  • Win32 程序在启动时激活前一个启动程序的窗口
  • C#/.NET 读取或修改文件的创建时间和修改时间
  • 通过解读 WPF 触摸源码,分析 WPF 插拔设备触摸失效的问题(问题篇)
  • .NET 中各种混淆(Obfuscation)的含义、原理、实际效果和不同级别的差异(使用 SmartAssembly)
  • .NET 中 GetProcess 相关方法的性能
  • 常用输入法快速输入自定义格式的时间和日期(搜狗/QQ/手心/微软拼音)
  • 好的框架需要好的 API 设计 —— API 设计的六个原则
  • .NET/C# 使用反射注册事件
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • Angular 响应式表单之下拉框
  • CentOS 7 修改主机名
  • eclipse(luna)创建web工程
  • Golang-长连接-状态推送
  • Javascript 原型链
  • JAVA并发编程--1.基础概念
  • LeetCode18.四数之和 JavaScript
  • Promise面试题2实现异步串行执行
  • React-Native - 收藏集 - 掘金
  • SOFAMosn配置模型
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 基于HAProxy的高性能缓存服务器nuster
  • 跨域
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 批量截取pdf文件
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 想写好前端,先练好内功
  • 小程序button引导用户授权
  • ​io --- 处理流的核心工具​
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (3)nginx 配置(nginx.conf)
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (一)appium-desktop定位元素原理
  • (转) 深度模型优化性能 调参
  • (转)JAVA中的堆栈
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .Net Remoting(分离服务程序实现) - Part.3
  • .NET Standard 的管理策略
  • .NetCore 如何动态路由
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • [04] Android逐帧动画(一)
  • [20150629]简单的加密连接.txt
  • [Android Pro] listView和GridView的item设置的高度和宽度不起作用
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)
  • [C# 开发技巧]实现属于自己的截图工具
  • [codevs 2822] 爱在心中 【tarjan 算法】