第 2 章:构建“长角”应用程序
布伦特·雷克托
明智猫头鹰咨询
2003 年 11 月
内容
Microsoft .NET 生成引擎:MSBuild.exe
使用 MSBuild 生成 Hello World
MSBuild 术语
生成 Longhorn 可执行文件应用程序
生成 Longhorn 库程序集
生成 Longhorn 文档
作为类声明的 XAML 文件
应用程序清单
部署清单
运行应用程序
为什么创建另一个生成系统?
总结
若要生成 Longhorn 应用程序,需要安装 Longhorn 软件开发工具包 (SDK)。 或者,可以安装支持 Longhorn 的® Microsoft Visual Studio® 版本。 在本书中,我不会讨论如何使用 Visual Studio,因为它的向导、花哨的代码生成功能和项目生成功能掩盖了实际上在幕后发生的事情。 我相信,在依赖该工具之前,你应该了解工具为你做什么。
安装 Longhorn SDK 时,它会创建一组“开始”菜单项,可用于创建一个命令提示符会话,在其中可以生成 Longhorn 应用程序。 若要在 Microsoft Windows® XP 32 位系统上生成应用程序的调试版本,请浏览以下菜单项以创建相应的命令提示符会话:
- 开始
- 程序
- Microsoft Longhorn SDK
- 打开“生成环境”窗口
- Windows XP 32 位生成环境
- 设置 Windows XP 32 位生成环境(调试)
Microsoft .NET 生成引擎:MSBuild.exe
MSBuild 是用于生成 Longhorn 应用程序的主要工具。 可以使用 help 命令行选项运行 MSBuild,以获取有关其用法的详细信息:
MSBuild /?
在没有任何命令行参数的情况下执行 MSBuild 时,它会在当前工作目录中搜索以“proj”结尾的文件名,例如 .proj、.csproj 等。找到项目时,它会根据该文件中的指令生成项目。
MSBuild
在目录中有多个项目文件时,可以在命令行上指定相应的项目文件:
MSBuild <ProjectName>.proj
通常,MSBuild 在项目文件中生成默认目标。 可以重写此值并指定要生成的目标。 例如,若要生成名为 cleanBuild 的目标,请调用 MSBuild,如下所示:
MSBuild /t:Cleanbuild
使用 MSBuild 生成 Hello World
让我们看看创建基于导航的简单 Hello World 应用程序所需的文件。 稍后,我将详细介绍每个文件的用途和用法。
首先,需要定义 应用程序 对象。 在通常称为 应用程序定义文件的文件中执行此操作。 此 HelloWorldApplication.xaml 文件定义我的 Application 对象。
HelloWorldApplication.xaml
<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml"
StartupUri="HelloWorld.xaml" />
此定义说:“对于 应用程序 对象,我想使用 MSAvalon.Windows.Navigation.NavigationApplication 类的实例。 启动时,应用程序应导航到 HelloWorld.xaml 文件中定义的用户界面(UI)。
下面是 HelloWorld.xaml 文件的内容。 这是第 1 章中上一个 Hello World 示例的稍微更有趣的版本。
HelloWorld.xaml
<Border xmlns="https://schemas.microsoft.com/2003/xaml">
<FlowPanel>
<SimpleText Foreground="DarkRed" FontSize="14">Hello World!</SimpleText> </FlowPanel>
</Border>
现在,我拥有简单 Hello World 应用程序的所有“代码”,我需要一个定义如何生成应用程序的项目文件。 下面是我的 HelloWorld.proj 文件。
HelloWorld.proj
<Project DefaultTargets="Build">
<PropertyGroup>
<Property Language="C#" />
<Property DefaultClrNameSpace="IntroLonghorn" />
<Property TargetName="HelloWorld" />
</PropertyGroup>
<!--Imports the target which contains all the common targets-->
<Import Project="$(LAPI)\WindowsApplication.target" />
<ItemGroup>
<!-- Application markup -->
<Item Type="ApplicationDefinition" Include="HelloWorldApplication.xaml" />
<!-- Compiled Xaml Files list -->
<Item Type="Pages" Include="HelloWorld.xaml"/>
</ItemGroup>
</Project>
将这三个文件放在目录中。 打开 Longhorn SDK 命令提示符,导航到包含文件的目录,并运行 MSBuild。 它将程序编译为可执行文件。
我们将在本章的后面部分检查应用程序定义文件的内容。 第 3 章详细介绍了许多可用于定义 UI 的可扩展应用程序标记语言 (XAML) 元素。 在更深入地查看项目文件之前,让我们来查看一些 MSBuild 术语。
MSBuild 术语
让我们为某些 MSBuild 元素建立定义。 属性 是键值对。 属性的值可能源自环境变量、命令行开关或项目文件中 属性 定义,如下所示:
<Property OutputDir="bin\" />
可以将 项 视为文件数组。 项 可以包含通配符,并且可以排除特定文件。 MSBuild 使用 项 的 Type 属性对项目进行分类,如下所示:
<Item Type="Compile" Include="*.cs" Exclude="DebugStuff.cs" />
任务 是生成过程中的原子单元。
任务 可以接受来自 属性 元素、项 元素或纯字符串的输入参数。
任务 的 名称 标识执行 任务所需的生成 .NET 数据类型。
- .NET 工具任务
- 部署任务
- Shell 任务
例如,
<Task Name="Csc" AssemblyName="$(OutputDir)\HelloWorld.exe"
Sources="@(Compile)" />
目标 是生成过程中的单个逻辑步骤。
目标 可以执行时间戳分析。 这意味着,如果不需要目标,
<Target Name="CopyToServer"
Inputs="$(OutputDir)\HelloWorld.exe"
Outputs="\\DeployServer\$(BuildNum)\HelloWorld.exe"
DependsOnTargets="Compile">
<Task Name="Copy" ... />
</Target>
如果 语句,
<PropertyGroup Condition=" '$(Configuration)'=='Debug' " >
<Property ... />
<Property ... />
</PropertyGroup>
Import 大致等效于 C/C++ #include 语句,如以下示例所示。 导入项目时,导入项目的内容在逻辑上将成为导入项目的一部分。
<Import Project="$(LAPI)\WindowsApplication.target" />
现在,术语已过期,接下来让我们检查一个典型的项目文件。
生成 Longhorn 可执行文件应用程序
下面是一个简单但相对全面的项目文件,用于生成可执行的 Longhorn 应用程序:
<Project DefaultTargets="Build">
<PropertyGroup>
<Property Language="C#" />
<Property DefaultClrNameSpace="IntroLonghorn" />
<Property TargetName="MyApp" />
</PropertyGroup>
<Import Project="$(LAPI)\WindowsApplication.target" />
<ItemGroup>
<Item Type="ApplicationDefinition" Include="MyApp.xaml" />
<Item Type="Pages" Include="HomePage.xaml" />
<Item Type="Pages" Include="DetailPage.xaml" />
<Item Type="Code" Include="DetailPage.xaml.cs"/>
<Item Type="DependentProjects" Include="MyDependentAssembly.proj" />
<Item Type="Components" Include="SomeThirdParty.dll" />
<Item Type="Resources" Include="Picture1.jpg"
FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
FileStorage="embedded" Localizable="True"/>
</ItemGroup>
</Project>
Project 元素
所有项目文件都以名为 Project的根元素定义开头。 其 DefaultTargets 属性指定系统在未指定目标时应生成的目标的名称。 在此示例中,我指定,默认情况下,系统应生成名为 生成的目标。
PropertyGroup 和 属性 元素
生成规则可以根据属性值有条件地执行。 如前所述,属性的值可能源自环境变量、MSBuild 命令行开关或项目文件中的属性定义。
应用程序的项目必须至少指定 语言 和 TargetName 属性的值。 在此示例中,我指定语言为 C# 并且生成的应用程序的名称应 MyApp。 我还向名为 DefaultClrNameSpace 的属性分配了一个值。
生成系统将每个 XAML 文件编译为托管类定义。 默认情况下,托管类的名称将与 XAML 源文件的基文件名相同。 例如,文件 Markup.xaml 编译为名为标记
我在导入其他项目之前定义了属性,因此导入项目中的规则将使用指定的属性值,例如,我将获得 C# 应用程序的正确生成规则,因为我将 语言 属性定义为 C#。
Import 元素
Build 目标中的规则生成我的 Longhorn 应用程序的可执行文件。 在每个项目文件中指定这些生成规则将是繁琐和重复的。 因此,稍后在项目文件中,我使用以下定义导入名为 WindowsApplication.target的预定义项目文件:
<Import Project="$(LAPI)\WindowsApplication.target" />
此导入的文件包含用于生成 Windows 应用程序的标准生成规则,它(间接)定义名为 生成的目标。
ItemGroup 和 Item 元素
ItemGroup 元素及其子 Item 元素定义生成应用程序所需的所有部件。
必须有一个 项,其 类型ApplicationDefinition,如下所示。 此 项 指定用于应用程序的 应用程序 对象的文件。 Application 对象通常是 MSAvalon.Windows.Application 类或 MSAvalon.Windows.Navigation.NavigationApplication 类的实例,这两个实例都在本章后面介绍。
<Item Type="ApplicationDefinition" Include="MyApp.xaml" />
每个 项类型页面 定义一组 XAML 文件,如下所示。 生成系统将这些 XAML 定义编译为包含在生成的程序集中的类。
<Item Type="Pages" Include="HomePage.xaml" />
<Item Type="Pages" Include="DetailPage.xaml" />
每个 项类型代码 表示源文件,如下所示。 生成系统使用项目 语言 属性选择的相应编译器编译这些源文件。
<Item Type="Code" Include="DetailPage.xaml.cs"/>
此项目可能依赖于其他项目。 生成系统必须编译这些依赖项目,然后才能生成此项目。 使用具有 DependentProjects类型 的 项 列出每个此类依赖项目:
<Item Type="DependentProjects" Include="MyDependentAssembly.proj" />
此项目中的代码可能使用预生成的程序集中的类型,也称为 组件程序集。 若要使用此类组件程序集编译代码,编译器需要引用每个程序集。 此外,在部署应用程序时,还需要部署这些组件程序集。 使用 项 列出每个组件程序集,类型组件:
<Item Type="Components" Include="SomeThirdParty.dll" />
引用的程序集与组件程序集有些不同。 在这两种情况下,代码都使用预生成的程序集中的类型。 但是,不会将引用的程序集作为应用程序的一部分交付,而将组件程序集作为应用程序的一部分交付。 生成系统需要知道这种区别。
可以使用 类型引用 指定 项,以指示编译器必须在生成时引用指定的程序集,如下所示,但程序集不会是应用程序部署的一部分。 生成系统会自动包括对标准系统程序集的引用,例如,mscorlib.dll、System.dll、PresentationFramework.dll。 等等 , 但必须添加应用程序必须引用的任何非标准程序集。
<Item Type="References" Include="SharedThirdParty.dll" />
应用程序可能还会使用资源。 项类型资源 描述应用程序使用的资源,如下所示。 生成系统可以将资源嵌入到生成的程序集中,或将其作为独立文件包含。 生成系统还可以将可本地化的资源放入附属程序集。
<Item Type="Resources" Include="Picture1.jpg"
FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
FileStorage="embedded" Localizable="True"/>
生成 Longhorn 库程序集
除了可执行应用程序之外,还需要生成库。 应用程序项目和库项目之间的主要区别如下:
- 库项目将 TargetType 属性的值设置为 库。
- 库项目通常不包括应用程序定义项。
下面是创建库的项目文件的示例:
<Project DefaultTargets="Build">
<PropertyGroup>
<Property Language="C#" />
<Property DefaultClrNameSpace="IntroLonghorn" />
<Property TargetName="MyLibrary" />
<Property TargetType="Library" />
</PropertyGroup>
<Import Project="$(LAPI)\WindowsApplication.target" />
<ItemGroup>
<Item Type="Pages" Include="ErrorPage.xaml" />
<Item Type="Code" Include="ErrorPage.xaml.cs"/>
<Item Type="Code" Include="Utilities.cs"/>
<Item Type="DependentProjects" Include="MyDependentAssembly.proj" />
<Item Type="Components" Include="SomeThirdParty.dll" />
<Item Type="Resources" Include="Picture1.jpg"
FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
FileStorage="embedded" Localizable="True"/>
</ItemGroup>
</Project>
生成 Longhorn 文档
不限于使用 XAML 生成应用程序。 还可以使用 XAML 文件创建高度交互式、智能呈现的自适应文档供用户阅读。 在这种情况下,XAML 文件共同表示文档的页面。 可以使用 MSBuild 引擎生成此类文档。
要生成文档的项目文件(而不是应用程序)的更改是次要的:
- 将 TargetType 属性的值设置为 Document。
- 为相应的生成规则导入 WindowsDocument.target 项目。
- 像往常一样包括所有其他项目文件。
请务必了解 文档 真正生成的 TargetType。 生成 文档时,生成输出是 .container 文件,生成系统可优化容器的内容以供下载,而不是速度。 容器文件是 Windows 结构化存储(也称为 DocFile)格式的扩展。 Longhorn 容器处理提供允许呈现部分下载文件的功能。 因此,在应用程序开始运行之前,无需下载整个容器。
此外,当要求 MSBuild 创建容器文件时,它将每个 XAML 文件编译为 XML 的二进制表示形式,称为二进制 XAML(BAML)。 BAML 比原始文本文件或已编译的to-IL 程序集要紧凑得多。 BAML 文件下载速度更快(已针对下载进行优化),但解释器必须在运行时对其进行分析,才能创建文件中所述的类的实例。 因此,此类文件未针对速度进行优化。 到目前为止,我一直在生成编译to-IL 文件(也称为 CAML 文件,短于已编译的 XAML)。
下面是创建电子文档的项目文件的示例:
<Project DefaultTargets="Build">
<PropertyGroup>
<Property TargetType="Document" />
<Property Language="C#" />
<Property DefaultClrNameSpace="IntroLonghorn" />
<Property TargetName="MyDocument" />
</PropertyGroup>
<Import Project="$(LAPI)\WindowsDocument.target" />
<ItemGroup>
<Item Type="ApplicationDefinition" Include="MyApp.xaml" />
<Item Type="Pages" Include="Markup.xaml" />
<Item Type="Pages" Include="Navigate.xaml" />
<Item Type="Code" Include="Navigate.xaml.cs"/>
<Item Type="Resources" Include="Picture1.jpg"
FileStorage="embedded" Localizable="False"/>
<Item Type="Resources" Include="Picture2.jpg"
FileStorage="embedded" Localizable="True"/>
</ItemGroup>
</Project>
了解如何生成各种类型的 Longhorn 应用程序和组件后,让我们更详细地了解 XAML 文件。 具体而言,让我们看看生成系统在将 XAML 文件转换为 .NET 类时执行的操作。
作为类声明的 XAML 文件
应用程序定义文件是定义应用程序的 Application 对象的类的 XAML 文件。 但是,一般情况下,XAML 文档只是定义类的文件。 XAML 定义生成的类派生自与 XML 文档的根元素名称关联的类。 默认情况下,生成系统使用 XAML 基文件名作为生成的类名。
为导航应用程序创建应用程序定义文件
回想一下,ApplicationDefinition 具有 Type 的 Item 元素指定定义 Application 对象的 XAML 文件的名称。 换句话说,此元素指定包含应用程序的入口点的 XAML 文件。 Longhorn 平台将创建 MSAvalon.Windows.Application派生类型的实例,该类型在此文件中定义,并允许它管理应用程序的启动、关闭和导航。
第 1 章介绍了如何以编程方式创建和使用应用程序实例。 以下 XAML 文件使用标记为项目定义 应用程序 对象:
<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml"
StartupUri="HelloWorld.xaml" />
我预计大多数 Longhorn 应用程序都是基于导航的应用程序,因此,通常只需重复使用标准 NavigationApplication 对象。 只需更改 StartupUri 属性的值,即可将此应用程序定义文件重新用于大多数基于导航的应用程序。
例如,如果以前的应用程序定义驻留在 HelloWorldApplication.xaml 文件中,并且我使用前面列出的 HelloWorld.proj 项目文件,生成系统将生成以下类声明:
namespace IntroLonghorn {
class HelloWorldApplication :
MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
}
}
命名空间由项目文件中 DefaultClrNameSpace 声明的结果,声明的类名与基文件名相同,声明的类扩展 XAML 文件中根元素所表示的类。
使用属性自定义生成的代码
在 XAML 文件中声明根元素时,可以使用根元素上的属性来控制生成的类声明的名称。 可以使用以下任一可选属性:
- 一个命名空间前缀定义,将前缀与名为 定义的命名空间相关联。 必须为此命名空间定义前缀才能使用 语言 和 类 属性。 传统上,使用 def 前缀。
- 语言 属性(在 定义 命名空间中定义)指定 XAML 文件中任何内联代码使用的编程语言。
- 一个 类 属性(在 定义 命名空间中定义),用于指定生成的类的名称。 指定包含一个或多个句点的名称时,生成系统不会将名称作为前缀,DefaultClrNameSpace 值。
例如,让我们将 HelloWorldApplication.xaml 文件的内容更改为以下内容:
<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
def:Class="Special.MyApp"
def:CodeBehind="HelloWorldApplication.xaml.cs"
StartupUri="HelloWorld.xaml" />
然后,生成的类将是:
namespace Special {
class MyApp :
MSAvalon.Windows.Navigation.NavigationApplication {
.
.
.
}
}
在同一类中使用代码和标记
除了指定 UI 的标记之外,几乎所有应用程序都需要编写一些代码(例如,按钮或虚拟方法重写的单击事件处理程序)。 回顾第 1 章,我的非基于导航的应用程序重写了 OnStartingUp 方法来创建其窗口和控件。 我将重写该示例来说明如何组合应用程序代码和标记。
虽然这个即将推出的示例创建了非导航应用程序,但我想强调,创建此类应用程序确实没有令人信服的理由。 始终可以创建从不实际导航到其他页面的基于导航的应用程序。 但是,编写此类应用程序需要我在同一类中混合代码和标记,因此提供了一个很好的示例。
回想一下,创建非导航应用程序需要定义一个从 msAvalon.Windows.Application 继承的自定义类,并替代 OnStartingUp 方法。 应用程序定义文件声明程序使用的应用程序对象类。 因此,非导航应用程序必须在同一类中定义其重写 OnStartingUp 方法。
除以下更改外,非导航应用程序的应用程序配置文件包含与导航应用程序定义文件相同的项:
- 根元素是 Application 而不是 NavigationApplication。
- 该文件必须包含或引用应用程序类的 OnStartingUp 方法的实现。
由于我需要使用标记和代码来实现单个类,因此我需要向你展示一种将源代码文件与 XAML 文件关联的技术。
将 Source-Behind 文件与 XAML 文件相关联
你经常希望使用标记开发应用程序的部分,并使用更传统的编程语言开发其他部分。 我强烈建议使用以下技术将 UI 和逻辑分离到单个源文件中。
可以将 XAML CodeBehind 元素(在 定义 命名空间中定义)添加到任何 XAML 文件的根元素,并指定源代码文件的名称(也称为 代码隐藏文件)。 生成引擎会将 XAML 声明编译为托管类。 生成系统还会将代码隐藏文件编译为托管类声明。 棘手的是,这两个类声明都表示单个类的分部声明。
下面是一个 XAML 定义,它生成与第 1 章中的第一个示例等效的非导航应用程序类:
<Application xmlns="https://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
def:Language="C#"
def:Class="IntroLonghorn.CodeBehindSample"
def:CodeBehind="CodeBehind.xaml.cs" />
此应用程序定义文件有两个值得注意的方面:
- 语言 属性指定代码隐藏文件包含 C# 源代码。
- CodeBehind 属性指定源文件名称CodeBehindMySample2.xaml.cs。
下面是匹配的源隐藏文件:
namespace IntroLonghorn {
using System;
using MSAvalon.Windows;
using MSAvalon.Windows.Controls;
using MSAvalon.Windows.Media;
public partial class CodeBehindSample {
MSAvalon.Windows.Controls.SimpleText txtElement;
MSAvalon.Windows.Window mainWindow;
protected override
void OnStartingUp (StartingUpCancelEventArgs e) {
base.OnStartingUp (e);
CreateAndShowMainWindow ();
}
private void CreateAndShowMainWindow () {
// Create the application's main window
mainWindow = new MSAvalon.Windows.Window ();
// Add a dark red, 14 point, "Hello World!" text element
txtElement = new MSAvalon.Windows.Controls.SimpleText ();
txtElement.Text = "Hello World!";
txtElement.Foreground = new
MSAvalon.Windows.Media.SolidColorBrush (Colors.DarkRed);
txtElement.FontSize = new FontSize (14,
FontSizeType.Point);
mainWindow.Children.Add (txtElement);
mainWindow.Show ();
}
}
}
请注意代码隐藏文件中类声明中 部分 关键字。 此关键字指出编译器应将此类定义与同一类的其他定义合并。 这样,你就可以提供一个类的多个部分定义,每个部分定义都位于单独的源文件中,编译器将其合并到生成的程序集中的单个类定义中。
在单个 XAML 文件中混合源代码和标记
我认为在同一文件中混合源代码和标记是错误的。 我甚至认为没有向你展示如何做到这一点。 然而,一些邪恶者会用这种技术编写一个示例程序,所以你可能需要了解他做了什么。 此外,可以使用前面所述的代码隐藏文件方法来消除少量邪恶的世界,并将 UI 与逻辑分开。
下面是一个应用程序定义文件,其中包含直接插入到标记内联的源代码:
<Application xmlns="https://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
def:Language="C#"
def:Class="IntroLonghorn.MySample2" >
<def:Code>
<![CDATA[
protected override void OnStartingUp (StartingUpCancelEventArgs e) {
base.OnStartingUp (e);
CreateAndShowMainWindow ();
}
. . . Remaining methods elided for clarity
]]>
</def:Code>
</Application>
在此示例中,Language 属性指定内联源代码为 C# 。 请注意,Code 元素是包含内联源代码的 CDATA 块。 有时,需要将内联源代码括在 XML CDATA 块中,以确保文档格式正确。 事实上,XAML 分析程序始终要求你将内联源代码括在 CDATA 块中,即使省略它生成格式正确的文档也是如此。
我再次为向你展示这样一种嘲弄而道歉。
应用程序清单
编译应用程序时,MSBuild 将生成 .exe 文件以及两个清单文件:应用程序清单、*.manifest 和部署清单 *.deploy。 从服务器部署应用程序或文档时,可以使用这些清单。 首先,将应用程序及其所有依赖项以及两个清单文件复制到服务器上的相应位置。 其次,编辑部署清单,使其指向应用程序清单的位置。
为了了解完整性,让我们看看应用程序和部署清单的示例。 以下示例中显示的应用程序清单实际上不像部署清单那样有趣。 应用程序清单只是定义构成应用程序的所有部件。 MSBuild 在生成应用程序时生成应用程序清单,并且通常会对其进行少量或根本没有修改。
HelloWorld.manifest
<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">
<assemblyIdentity name="HelloWorld" version="1.0.0.0"
processorArchitecture="x86" asmv2:culture="en-us"
publicKeyToken="0000000000000000" />
<entryPoint name="main" xmlns="urn:schemas-microsoft-com:asm.v2"
dependencyName="HelloWorld">
<commandLine file="HelloWorld.exe" parameters="" />
</entryPoint>
<TrustInfo xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:temp="temporary">
<Security>
<ApplicationRequestMinimum>
<PermissionSet class="System.Security.PermissionSet" version="1"
ID="SeeDefinition">
<IPermission
class="System.Security.Permissions.FileDialogPermission"
version="1" Unrestricted="true" />
<IPermission
class="System.Security.Permissions.IsolatedStorageFilePermission"
version="1" Allowed="DomainIsolationByUser" UserQuota="5242880" />
<IPermission
class="System.Security.Permissions.SecurityPermission"
version="1" Flags="Execution" />
<IPermission
class="System.Security.Permissions.UIPermission" version="1"
Window="SafeTopLevelWindows" Clipboard="OwnClipboard" />
<IPermission
class="System.Security.Permissions.PrintingPermission"
version="1" Level="SafePrinting" />
<IPermission
class="MSAvalon.Windows.AVTempUIPermission, PresentationFramework,
Version=6.0.4030.0, Culture=neutral,
PublicKeyToken=a29c01bbd4e39ac5" version="1"
NewWindow="LaunchNewWindows" FullScreen="SafeFullScreen" />
</PermissionSet>
<AssemblyRequest name="HelloWorld"
PermissionSetReference="SeeDefinition" />
</ApplicationRequestMinimum>
</Security>
</TrustInfo>
<dependency asmv2:name="HelloWorld">
<dependentAssembly>
<assemblyIdentity name="HelloWorld" version="0.0.0.0"
processorArchitecture="x86" />
</dependentAssembly>
<asmv2:installFrom codebase="HelloWorld.exe"
hash="5c58153494c16296d9cab877136c3f106785bfab"
hashalg="SHA1" size="5632" />
</dependency>
</assembly>
应用程序清单的大部分内容应该相对明显。 entryPoint 元素指定入口点方法的名称,主,并引用包含入口点的 依赖项(HelloWorld)。 entryPoint 元素还包含 shell 需要运行应用程序的程序名称和命令行参数。
HelloWorld依赖项 元素包含信息(dependentAssembly 元素),该信息指定依赖程序集和 installFrom 元素,该元素指示加载程序在何处查找程序集的文件和文件的原始哈希。 加载程序可以使用哈希来检测在编译后对程序集所做的更改。
Longhorn 信任管理器使用 TrustInfo 元素来确定应用程序所需的安全权限。 在前面的示例中,我的 HelloWorld 应用程序定义了一组权限,它将其命名为 seeDefinition 权限集
部署清单
如前所述,部署清单更有趣。 部署清单显然包含控制应用程序的部署的设置。
HelloWorld.deploy
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd">
<assemblyIdentity name="HelloWorld.deploy" version="1.0.0.0"
processorArchitecture="x86" asmv2:culture="en-us"
publicKeyToken="0000000000000000" />
<description asmv2:publisher="Wise Owl, Inc."
asmv2:product="Brent's HelloWorld Application"
asmv2:supportUrl="http://www.wiseowl.com/AppServer/HelloWorld/support.asp"
/>
<deployment xmlns="urn:schemas-microsoft-com:asm.v2"
isRequiredUpdate="false">
<install shellVisible="true" />
<subscription>
<update>
<beforeApplicationStartup />
<periodic>
<minElapsedTimeAllowed time="6" unit="hours" />
<maxElapsedTimeAllowed time="1" unit="weeks" />
</periodic>
</update>
</subscription>
</deployment>
<dependency>
<dependentAssembly>
<assemblyIdentity name="HelloWorld" version="1.0.0.0"
processorArchitecture="x86" asmv2:culture="en-us"
publicKeyToken="0000000000000000" />
</dependentAssembly>
<asmv2:installFrom codebase="HelloWorld.manifest" />
</dependency>
</assembly>
部署清单包含 Longhorn 安装和更新应用程序所需的信息。 请注意,部署清单 assemblyIdentity 元素引用应用程序清单。 毕竟,应用程序清单已经描述了应用程序的所有组件。 若要安装应用程序,部署清单实际上表示,“下面是安装此应用程序所需的文件的说明。
当然,当你安装应用程序时,你还需要更多信息,而不仅仅是要复制到系统上的文件。 说明 元素列出了 发布者、产品,以及 supportUrl 属性;系统在“添加/删除程序”对话框中显示其内容。
部署 元素指定如何在部署后部署和更新应用程序。 在此示例中,应用程序对 shell 可见,客户端的系统将在用户每次启动应用程序时检查并根据需要下载应用程序的新版本。 此外,系统定期(不超过每 6 小时且每周不超过一次)检查新版本。 当定期检查找到新版本时,Longhorn 将在后台下载新版本并安装它;然后,它将准备好在下次用户执行应用程序时运行。
运行应用程序
通常,用户将“运行”应用程序清单,直接从服务器执行应用程序,而无需在本地计算机上安装应用程序。 Longhorn 根据需要下载应用程序的组件。 在这种情况下,服务器必须可用于运行应用程序。
当用户“运行”部署清单时,Longhorn 在本地计算机上下载并安装应用程序。 应用程序可以在桌面上安装图标、添加“开始”菜单项,并且通常成为传统的已安装应用程序。 当然,你还将获得自动后台更新、版本回滚和卸载支持。
首次启动部署清单时,Longhorn 会在应用程序缓存中安装应用程序,并将条目添加到控制面板的“添加或删除程序”列表中。 随后,每当运行部署清单时,应用程序会直接从应用程序缓存加载。 它通常不会再次下载。
但是,使用控制面板的“添加/删除程序”小程序小程序从缓存中卸载应用程序时,随后会再次执行部署清单下载并安装应用程序。
或者,可以更改服务器上的应用程序的版本号。 然后,运行部署清单时,Longhorn 将下载并安装与当前版本并排安装新版本。 应用程序的两个版本将显示在“添加或删除程序”列表中。
为什么创建另一个生成系统?
我真的很喜欢 MSBuild,尽管在撰写本文时,我只有几个星期的经验。 当然,多年的制作文件经验使任何更优雅的生成系统更具吸引力。 目前,共有两个备用生成系统- Make 和 Ant。 将 MSBuild 与此类替代方案进行比较似乎很自然。
为什么不使用 Make?
当许多开发人员熟悉一个名为 Make 的现有生成系统时,为什么要开发新的生成系统? 使工具集成到生成系统中很差。 简单地执行 shell 命令。 因此,在生成过程中,一个工具无法与另一个工具通信。 MSBuild 创建 任务 类的实例,任务可以在它们之间相互通信,从而传递正常的 .NET 类型。
生成文件具有异常语法,难以编写,并且无法很好地缩放,因为它们对于大型项目来说很复杂。 此外,Make 以外的工具无法轻松处理生成文件。 MSBuild 以外的工具可以轻松生成和分析 MSBuild 项目的基于 XML 的语法。
最后,Make 实际上对项目没有支持。 没有文件系统抽象,也不支持级联属性。 此外,没有生成生成文件的设计时支持。
为什么不使用蚂蚁?
类似的常见问题是,当存在一个名为 Ant 的现有非常成功且丰富的系统时,为什么要开发一个新的基于 XML 的生成系统? Ant 是一个 Java 开源生成系统,来自 Apache.org,它率先将基于 XML 的项目文件和任务作为原子生成操作。 还有一个伟大的 .NET 端口的 Ant,称为 NAnt 从 nant.sourceforge.net。 在表面上,MSBuild 和 Ant/NAnt 相似。 这两种工具都使用 XML 作为项目序列化格式,两个工具都使用任务作为其生成操作的原子单元。 这两种工具都有其优势,但当你仔细观察时,它们有不同的设计目标。
Ant 做出了设计决策,将许多功能放入一组大型任务中。 MSBuild 具有不同的设计目标,其中引擎封装了类似的功能(例如时间戳分析、通过项之间的任务通信、任务批处理、项转换等)。 这两种方法都有他们的长处和弱点。
Ant 的模型允许开发人员扩展和控制生成的每个细节,因此非常灵活。 然而,它还对任务编写器负有更大的责任,因为任务需要更加复杂,以提供一致的功能。 MSBuild 的模型减少了每个任务需要实现的功能量。 因此,项目作者可以依赖于不同项目、目标和任务的一致功能。 此外,集成开发环境(如 Visual Studio)还可以依赖这些服务来提供一致的结果和丰富的用户体验,而无需知道生成过程中调用的任务的内部内容。
同样,虽然 Ant 具有生成脚本的概念,但它没有 MSBuild 具有的项目清单的概念。 生成脚本说明如何创建一组文件,但不提供描述文件使用方式的其他语义。 清单还描述了文件的语义,它允许其他工具(如 IDE)更深入地与生成系统集成。 相反,缺少项目清单意味着开发人员可以更轻松地定制 Ant 来生成新种类的“内容”,因为生成脚本没有约束架构。
总结
你现在已经掌握了基础知识。 可以编写 XAML,并且可以编译、部署和运行生成的应用程序。 不幸的是,到目前为止,你学习编写的应用程序相当无聊。 第 3 章 深入探讨 XAML,并演示如何使用 Longhorn 平台提供的各种 UI 对象。 后面的章节介绍了许多其他可用于应用程序中的新技术。