第 2 章:建置 「Longhorn」 應用程式

 

簡介
第 1 章:「Longhorn」 應用程式模型

第 2 章:建置 「Longhorn」 應用程式

Brent Rector
明智的 Owl 諮詢

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應用程式所需的檔案。 稍後,我將詳細說明每個檔案的用途和使用方式。

首先,您必須定義 Application 物件。 您通常會在稱為 應用程式定義檔的檔案中執行此動作。 這個 HelloWorldApplication.xaml 檔案會定義我的 Application 物件。

HelloWorldApplication.xaml

<NavigationApplication xmlns="https://schemas.microsoft.com/2003/xaml" 
                       StartupUri="HelloWorld.xaml" />

此定義指出:「針對我的 應用程式 物件,我想要使用 MSAvalon.Windows.Navigation.NavigationApplication 類別的實例。 啟動時,應用程式應該流覽至 ,並顯示使用者介面 (UI) 在 HelloWorld.xaml 檔案中定義。」

以下是 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 章中,我詳細說明許多可延伸應用程式標記語言 (XAML) 元素,可用來定義 UI。 在我們更深入地查看專案檔之前,讓我們先檢閱一些 MSBuild 術語。

MSBuild 術語

讓我們建立一些 MSBuild 元素的定義。 屬性是索引鍵/值組。 屬性值可能源自環境變數、命令列參數或專案檔中的屬性定義,如下所示:

<Property OutputDir="bin\" />

您可以將 Item 視為檔案陣列。 專案可以包含萬用字元,並可排除特定檔案。 MSBuild 會使用ItemType屬性來分類專案,如下所示:

<Item Type="Compile" Include="*.cs" Exclude="DebugStuff.cs" />

工作是建置程式中不可部分完成的單位。 Task可以接受來自 Property元素、Item元素或純字串的輸入參數。 工作的名稱會識別執行工作所需的組建 .NET資料類型。 工作可以發出其他工作取用的專案。 MSBuild 包含許多工作,其中所有工作都可以廣泛分類為

  • .NET 工具工作
  • 部署任务
  • 殼層工作

例如,使用Csc名稱的工作會叫用 C# 編譯器做為建置工具,它會編譯Sources屬性中指定的所有Item元素, (它會將具有編譯) 類型的專案專案指定為元件,並將元件產生為輸出專案。

<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>

Condition屬性大致相當於簡單的if語句。 條件可以比較兩個字串,或檢查檔案或目錄是否存在。 您可以將 Condition 套用至專案檔中的任何專案。 例如,以下是只有在 Configuration 屬性具有 Debug值時才定義的屬性群組:

<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 屬性會指定當您未指定目標時,系統應該建置的目標名稱。 在此範例中,我預設會指定系統應該建置名為 Build的目標。

PropertyGroupProperty Elements

建置規則可以根據屬性值有條件地執行。 如前所述,屬性值可能源自環境變數、MSBuild 命令列參數,或來自專案檔中的屬性定義。

應用程式的專案必須至少指定 LanguageTargetName 屬性的值。 在此範例中,我指定語言為 C#,且產生的應用程式名稱應該是 MyApp。 我也已將值指派給名為 DefaultClrNameSpace的屬性。

建置系統會將每個 XAML 檔案編譯成 Managed 類別定義。 根據預設,Managed 類別的名稱會與 XAML 原始程式檔的基底檔案名相同。 例如,Markup.xaml 檔案會編譯成名為 Markup的類別定義。 藉由將 DefaultClrNameSpace 屬性設定為 IntroLonghorn,我要求建置系統在產生的類別名稱前面加上 IntroLonghorn 命名空間。 因此,建置系統會針對 Markup.xaml 定義產生名為 IntroLonghorn.Markup 的類別。

我在匯入其他專案之前定義了屬性,因此匯入專案中的規則會使用我指定的屬性值,例如,我會取得 C# 應用程式的適當建置規則,因為我將 Language 屬性定義為 C#

Import元素

置目標中的規則會產生我的 Longhorn 應用程式的可執行檔。 在每個專案檔中指定這些建置規則會很繁瑣且重複。 因此,稍後在專案檔中,我使用下列定義匯入名為 WindowsApplication.target的預先定義專案檔:

  <Import Project="$(LAPI)\WindowsApplication.target" />

這個匯入的檔案包含建置 Windows 應用程式的標準建置規則, (間接) 定義名為 Build的目標。

ItemGroupItem 元素

ItemGroup元素及其子Item元素會定義建置應用程式所需的所有元件。

您必須有一個類型ApplicationDefinition的專案,如下所示。 此 專案 會指定要用於應用程式的 Application 物件的檔案。 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" />

每個具有程式碼類型的專案都代表原始程式,如下所示。 建置系統會使用專案 Language 屬性所選取的適當編譯器來編譯這些來源檔案。

<Item Type="Code" Include="DetailPage.xaml.cs"/>

此專案可能相依于其他專案。 建置系統必須先編譯這些相依專案,才能建置此專案。 您可以使用具有DependentProjects類型的專案來列出每個這類相依專案:

<Item Type="DependentProjects" Include="MyDependentAssembly.proj" /> 

此專案中的程式碼可能會使用預先建置元件中的類型,也稱為 元件元件。 若要使用這類元件元件編譯器代碼,編譯器需要每個元件的參考。 此外,當您部署應用程式時,也需要部署這些元件元件。 您可以使用具有元件類型的專案來列出每個元件元件:

<Item Type="Components" Include="SomeThirdParty.dll" />

參考的元件與元件元件稍有不同。 在這兩種情況下,您的程式碼會使用預先建置元件中的類型。 不過,您不會將參考的元件寄送為應用程式的一部分,而您會將元件元件寄送為應用程式的一部分。 建置系統必須知道這項區別。

您可以指定類型為References的專案,以指出編譯器必須在建置時參考指定的元件,如下所示,但元件不會是應用程式部署的一部分。 建置系統會自動包含標準系統元件的參考,例如,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 屬性的值設定為 Library
  • 程式庫專案通常不包含應用程式定義專案。

以下是建立程式庫的專案檔範例:

<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 專案。
  • 如往常包含所有其他專案檔。

請務必瞭解Document 的 TargetType真正會產生什麼。 當您建置 時,組建輸出是 .container 檔案,而建置系統會優化容器的內容以供下載,而不是速度。 容器檔案是 Windows 結構化儲存體的延伸模組,也稱為 DocFile 格式。 Longhorn 容器處理提供的功能允許轉譯部分下載的檔案。 因此,在應用程式開始執行之前,您不需要下載整個容器。

此外,當您要求 MSBuild 建立容器檔案時,它會將每個 XAML 檔案編譯成 XML 的二進位標記法,稱為二進位 XAML (BAML) 。 BAML 比原始文字檔或編譯到 IL 元件更精簡。 BAML 檔案下載更快速—已針對下載進行優化,但解譯器必須在執行時間剖析它們,才能建立檔案中所述類別的實例。 因此,這類檔案並未針對速度優化。 到目前為止,我已產生編譯到 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類型的Item元素會指定定義Application物件的 XAML 檔案名稱。 換句話說,此元素會指定包含應用程式進入點的 XAML 檔案。 Longhorn 平臺會建立您在此檔案中定義的 MSAvalon.Windows.Application衍生類型的實例,並讓它管理應用程式的啟動、關機和流覽。

在第 1 章中,您已瞭解如何以程式設計方式建立和使用應用程式實例。 下列 XAML 檔案會使用標記來定義專案的 Application 物件:

<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 檔案中宣告根項目時,可以使用根項目上的屬性來控制所產生類別宣告的名稱。 您可以使用下列任一選擇性屬性:

  • 命名空間前置詞定義,可將前置詞與名為 Definition的命名空間產生關聯。 您必須為此命名空間定義前置詞,才能使用 LanguageClass 屬性。 傳統上,會使用 def 前置詞。
  • 定義命名空間中所定義的Language屬性 () ,指定 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 的標記之外,幾乎所有應用程式都需要您撰寫一些程式碼,例如按鈕或虛擬方法覆寫的 Click 事件處理常式。 回想一下第 1 章,我的非導覽型應用程式會覆寫 OnStartingUp 方法,以建立其視窗和控制項。 我將重寫該範例,以說明如何結合應用程式程式碼和標記。

雖然這個即將推出的範例會建立非導覽應用程式,但我想要強調,建立這類應用程式並沒有吸引人的理由。 您一律可以建立永遠不會實際巡覽至不同頁面的導覽型應用程式。 不過,撰寫這類應用程式需要我混合相同類別中的程式碼和標記,因此提供良好的範例。

回想一下,建立非導覽應用程式需要您定義繼承自 MSAvalon.Windows.Application 的自訂類別,並覆寫 OnStartingUp 方法。 應用程式定義檔會宣告程式所使用的應用程式物件類別。 因此,非導覽應用程式必須在相同的類別中定義其覆寫 OnStartingUp 方法。

除了下列變更之外,非導覽應用程式的應用程式組態檔包含與導覽應用程式定義檔相同的專案:

  • 根項目是 Application ,而不是 NavigationApplication
  • 檔案必須包含或參考應用程式類別 OnStartingUp 方法的實作。

由於我需要使用標記和程式碼來實作單一類別,因此我需要示範將原始程式碼檔案與 XAML 檔案產生關聯的技術。

將Source-Behind檔案與 XAML 檔案產生關聯

您經常想要使用標記來開發應用程式的部分,並使用較傳統的程式設計語言來開發其他部分。 我強烈建議使用下列技術,將 UI 和邏輯分成個別的原始程式檔。

您可以將定義) 命名空間中定義的 XAML CodeBehind元素 (新增至任何 XAML 檔案的根項目,並指定原始程式碼檔的名稱 (也稱為程式代碼後置檔案) 。 建置引擎會將 XAML 宣告編譯成 Managed 類別。 建置系統也會將程式碼後置檔案編譯成 Managed 類別宣告。 棘手的層面是這兩個類別宣告都代表單一類別的部分宣告。

以下是一個 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" />

此應用程式定義檔有兩個值得注意的層面:

  • Language屬性會指定程式碼後置檔案包含 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 ();
    }
  }
}

請注意程式碼後置檔案中類別宣告中的 partial 關鍵字。 這個關鍵字指出編譯器應該將此類別定義與相同類別的其他定義合併。 這可讓您提供類別的多個部分定義,每個定義在個別的來源檔案中,編譯器會在產生的元件中合併成單一類別定義。

在單一 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元素會指定進入點方法、main的名稱,並參考包含進入點的相依性,名為HelloWorldentryPoint元素也包含殼層需要執行應用程式的程式名稱和命令列引數。

HelloWorld相依性元素包含 (dependentAssembly元素的資訊,) 指定相依元件和installFrom元素,告知載入器在何處尋找元件的檔案和檔案的原始雜湊。 載入器可以使用雜湊來偵測在編譯之後對元件所做的變更。

Longhorn 信任管理員會使用 TrustInfo 元素來判斷應用程式所需的安全性許可權。 在上一個範例中,我的 HelloWorld 應用程式會定義一組名稱為 SeeDefinition 許可權集合的許可權。 緊接在定義許可權集之後, AssemblyRequest 元素會要求名為 HelloWorld 的元件至少會收到一組名為 SeeDefinition的集合中的許可權。 此範例中的許可權是通常會授與在 SEE 中執行之應用程式的許可權,因此Hello World應用程式執行,而不會向使用者顯示任何信任管理員安全性警告。

部署資訊清單

如前所述,部署資訊清單更有趣。 部署資訊清單包含明顯足夠的設定,可控制應用程式的部署。

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 元素會參考應用程式資訊清單。 之後,應用程式資訊清單已經描述應用程式的所有元件。 若要安裝應用程式,部署資訊清單會顯示「以下是安裝此應用程式所需的檔案描述」。

當然,當您安裝應用程式時,除了要複製到系統上的檔案之外,還需要更多資訊。 description元素會列出發行者產品和supportUrl屬性;系統會在 [新增/移除程式] 對話方塊中顯示其內容。

部署元素會指定如何在部署後部署和更新應用程式。 在此範例中,殼層可以看到應用程式,而且用戶端的系統會在每次使用者啟動應用程式時下載新版本的應用程式,並視需要檢查和 。 此外,系統會定期檢查新版本,每六小時不超過一次,且每週不會少於一次。 當定期檢查找到新版本時,Longhorn 會在背景下載新版本並加以安裝;然後,它會準備好在下一次使用者執行應用程式時執行。

執行應用程式

一般而言,使用者會「執行」應用程式資訊清單,直接從伺服器執行應用程式,而不需在本機電腦上安裝應用程式。 Longhorn 會視需要下載應用程式的元件。 在此情況下,伺服器必須可供執行應用程式。

當使用者「執行」部署資訊清單時,Longhorn 會在本機電腦上下載並安裝應用程式。 應用程式可以在桌面上安裝圖示、新增 [開始] 功能表項目,而且通常會成為傳統的已安裝應用程式。 當然,您也會取得自動背景更新、版本復原和卸載支援。

當您第一次啟動部署資訊清單時,Longhorn 會在應用程式快取中安裝應用程式,並將專案新增至主控台的 [新增或移除程式] 清單。 之後,每當您執行部署資訊清單時,應用程式會直接從應用程式快取載入。 通常不會再次下載。

不過,當您使用主控台的 [新增/移除程式] 小程式從快取卸載應用程式時,後續會執行部署資訊清單下載並再次安裝應用程式。

或者,您也可以變更伺服器上的應用程式版本號碼。 然後,當您執行部署資訊清單時,Longhorn 會下載並安裝與目前版本並存的新版本。 這兩個版本的應用程式都會出現在 [新增或移除程式] 清單中。

為何要建立另一個組建系統?

我真的喜歡 MSBuild,雖然在撰寫本文時,我只有幾周的經驗。 當然,Makefiles 的年經驗讓任何更簡潔的建置系統更具吸引力。 目前有兩個常用的替代建置系統:Make 和 Ant。 比較 MSBuild 與這類替代方案似乎很自然。

為何不使用 Make?

當許多開發人員熟悉名為 Make 的現有組建系統時,為何要開發新的組建系統? 讓 將工具整合到建置系統中有不良的效能。 只要執行殼層命令即可。 因此,在建置程式期間,一個工具無法與另一個工具通訊。 MSBuild 會建立 Task 類別的實例,而工作可以在本身之間通訊,並傳遞一般 .NET 類型。

Makefiles 具有不尋常的語法、難以撰寫,而且無法妥善調整,因為它們對於大型專案而言很複雜。 此外,Make 以外的工具無法輕鬆地處理 Makefile。 MSBuild 以外的工具可以輕鬆地產生和剖析 MSBuild 專案的 XML 語法。

最後,Make 實際上不支援專案。 沒有檔案系統抽象概念,也不支援串聯屬性。 此外,沒有產生 makefile 的設計階段支援。

為何不使用 Ant?

有一個類似的常見問題,就是為什麼當現有的非常成功且豐富的系統稱為 Ant 時,為何要開發新的 XML 型建置系統? Ant 是 JAVA,開放原始碼建置系統,Apache.org 以 XML 為基礎的專案檔和工作作為不可部分完成的建置作業。 另外還有一個絕佳的 .NET 埠,稱為 NAnt 可從 nant.sourceforge.net取得。 在介面上,MSBuild 和 Ant/NAnt 類似。 這兩個工具都會使用 XML 做為其專案序列化格式,而且兩個工具都會使用工作作為其建置作業的不可部分完成單位。 這兩個工具都有其優點,但當您進一步瞭解它們有不同的設計目標時。

Ant 做出設計決策,將許多功能放在一組大型的工作中。 MSBuild 有不同的設計目標,其中引擎 (封裝類似的功能,例如時間戳記分析、透過專案間通訊、工作批次處理、專案轉換等等) 。 這兩種方法都有其優點和缺點。

Ant 的模型可讓開發人員擴充和控制組建的每個詳細資料,因此非常有彈性。 不過,它也會對工作寫入器承擔更大的責任,因為工作需要更複雜,才能提供一致的功能。 MSBuild 的模型可減少每個工作需要實作的功能數量。 因此,專案作者可以依賴不同專案、目標和工作之間的一致功能。 此外,Visual Studio 之類的整合式開發環境也可以依賴這些服務來提供一致的結果和豐富的使用者體驗,而不需要知道建置程式期間呼叫之工作內部的任何內容。

同樣地,雖然 Ant 具有建置腳本的概念,但它沒有 MSBuild 擁有的專案資訊清單概念。 建置腳本說明如何建立一組檔案,但不提供描述檔案使用方式的其他語意。 資訊清單也描述檔案的語意,可讓 IDE 等其他工具更深入地與建置系統整合。 相反地,缺少專案資訊清單表示開發人員可以更輕鬆地量身打造 Ant 來建置新種類的「內容」,因為建置腳本沒有限制架構。

總結

您現在已掌握基本概念。 您可以撰寫 XAML,並可編譯、部署和執行產生的應用程式。 不幸的是,您到目前為止學到撰寫的應用程式相當無聊。 第 3 章 深入探討 XAML,並示範如何使用 Longhorn 平臺所提供的各種 UI 物件。 稍後的章節將說明一些其他可用於應用程式中的新技術。

繼續進行第 3 章:控制項和 XAML