2018 年 5 月

第 33 卷,第 5 期

本文章是由機器翻譯。

通用 Windows 平台 - 縮小 UWP 與 Win32 之間的差距

Andrew Whitechapel

一個重要的佈景主題的最新版的 Windows update 已關閉的通用 Windows 平台 (UWP) 和傳統的 Win32 應用程式模型之間的差距。這項工作的一部分,Microsoft 導入三個主要的增強功能:

  • 多重執行個體
  • 主控台 UWP 應用程式
  • 更廣泛的檔案系統存取

功能都相關但多半獨立。也就是說,您可以建立規則的視窗型應用程式或主控台應用程式的多重執行個體應用程式,並可能會或可能不需要更廣泛的檔案系統的存取。同樣地,您可以建立一般的非主控台、 非多重-執行個體應用程式具有廣泛的檔案系統存取權。一項限制是主控台應用程式,必須設定為支援多重執行個體。

多重執行個體

在 Win32、 Linux 和其他應用程式模型環境中,多重執行個體一直是預設值。在 UWP 中黑白強烈對比,預設值一直單一執行個體,而且,事實上,多重執行個體不支援一個到目前為止。

不含多個執行個體會,某些應用程式會訴諸多重視窗化架構相反地,這通常會涉及相當多的工作,並且導致複雜性和脆弱數已超過。您需要花費很多心力來管理您的 windows,而不需要專注於您網域的需求。單一處理序的多重視窗化也會受到可靠性考量:如果損毀的單一執行個體,它會將其 windows;這不適用於多重執行個體,每個執行個體當做個別處理序的執行位置。

在單一執行個體模型中,使用者可以啟用應用程式的數種方法: 透過在開始; 磚點選透過 URL 或通訊協定的啟用。按兩下副檔名已向應用程式的檔案等等。第一個啟動 (任何) 啟動應用程式。之後,任何後續啟用只需呼叫應用程式可以藉由覆寫 OnActivated 方法處理的應用程式執行的執行個體。

多重執行個體的新功能可讓 UWP 應用程式,例如 Win32 應用程式運作:如果應用程式的執行個體正在執行,且收到後續的啟用要求時,平台不會呼叫啟動現有的執行個體。相反地,它會個別處理序中建立的新執行個體。

這項功能主要由 Win32 同位檢查所驅動的因為它是一開始只有支援桌面和 IoT。導入的支援多重執行個體的兩個層級:

  • 多個執行個體 UWP 應用程式:這是最簡單的情況下,其中應用程式只想要宣告它應該是多執行個體。
  • 多個執行個體重新導向 UWP 應用程式:這是最複雜的情況下,應用程式想要成為多重建立,但它也要有說中完全每個執行個體已啟動的方式。

這兩種情況下,Visual Studio 專案範本提供,如下所示圖 1

多重執行個體的應用程式的新專案範本
圖 1 多執行個體的應用程式的新專案範本

簡單的案例中,專案範本會產生空白的應用程式的範本程式碼幾乎完全相同的程式碼。唯一的差別是應用程式資訊清單中 SupportsMultipleInstances 屬性的使用中。有兩個額外的宣告:第一個是在資訊清單的頂端 desktop4 和 iot2 XML 命名空間:

xmlns:desktop4="https://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:iot2="https://schemas.microsoft.com/appx/manifest/iot/windows10/2"
  IgnorableNamespaces="uap mp desktop4 iot2">

第二個新增 < 應用程式 > 項目上的屬性:

<Application
  Id="App8" Executable="$targetnametoken$.exe" EntryPoint="App8.App"
  desktop4:SupportsMultipleInstances="true"
  iot2:SupportsMultipleInstances="true">

如果您更新現有的應用程式程式碼,而非產生新的應用程式,您可能會直接請手動加入資訊清單加入這些項目。一旦您已這麼做,您可以建置應用程式,並啟動多個執行個體。與此資訊清單項目,每次啟動應用程式,是否從點選磚或任何其他啟用合約應用程式支援,例如檔案關聯或通訊協定啟動 — 每個啟用會在個別的執行個體。就是這麼簡單。

多個執行個體重新導向

針對大部分的應用程式,您只需要加入資訊清單的項目,但想要更充分掌控其執行個體啟用應用程式中,您可以使用第二個範本。這會將正確的相同資訊清單項目,並也會加入其他檔案 (針對 C# 應用程式,Program.cs) 或 Program.cpp for c + + 包含標準的 Main 函式中所示圖 2。這會使用此版本中引進的新 AppInstance 類別。

圖 2 標準 Main 函式的多個執行個體重新導向

static void Main(string[] args)
{
  IActivatedEventArgs activatedArgs = AppInstance.GetActivatedEventArgs();
  if (AppInstance.RecommendedInstance != null)
  {
    AppInstance.RecommendedInstance.RedirectActivationTo();
  }
  else
  {
    uint number = CryptographicBuffer.GenerateRandomNumber();
    string key = (number % 2 == 0) ? "even" : "odd";
    var instance = AppInstance.FindOrRegisterInstanceForKey(key);
    if (instance.IsCurrentInstance)
    {
      global::Windows.UI.Xaml.Application.Start((p) => new App());
    }
    else
    {
      instance.RedirectActivationTo();
    }
  }
}

函式作用的第一件事是抓取這個執行個體啟用引數。應用程式很可能會使用這些引數保留做為其重新導向邏輯的一部分的資訊。

在某些情況下,平台可能表示建議的執行個體。如果是這樣,您可以重新導向此啟用該執行個體相反地,如果您想使用 AppInstance.RedirectActivationTo 方法。換句話說,應用程式可以選擇允許此啟用要求,改為重新導向至現有的執行個體。如果沒有重新導向,則目標執行個體已啟動,而且會叫用其 OnActivated 方法,並終止這個新的執行個體。

平台不會指出慣用的執行個體,如果您繼續,並撰寫金鑰。範例程式碼撰寫的索引鍵,從一個隨機數字,但您通常會取代此程式碼和撰寫應用程式定義的邏輯為基礎的索引鍵。通常這會根據先前擷取的啟用引數。比方說,如果啟用引數的型別 FileActivatedEventArgs,應用程式可能會使用指定的檔案名稱做為索引鍵的一部分。一旦您已撰寫的索引鍵,您將它傳遞至 FindOrRegisterInstanceForKey 方法,它會傳回 AppInstance 物件,表示此應用程式的執行個體。若要判斷要傳回的執行個體,該方法會執行兩件事:

  • 它會搜尋現有的執行個體已經註冊此機碼的應用程式。
  • 如果沒有現有的執行個體已經註冊此機碼,它會註冊此目前執行個體具有這個索引鍵。

如果您已成功註冊此執行個體,您可以現在繼續,進行一般應用程式初始化。XAML 應用程式中,這表示呼叫 Application.Start 與應用程式類別的新執行個體。其他執行個體已經註冊這個索引鍵,如果您現在可以相反地,將此啟用重新導向至該執行個體,並允許終止此執行個體。例如,假設應用程式編輯檔案。如果使用者具有 Foo.doc 編輯應用程式中開啟,並嘗試再次開啟 Foo.doc,應用程式可能會選擇將第二個啟用重新導向至已經具有 Foo.doc 開啟的執行個體,並防止使用者在從多個執行個體中開啟相同的檔案。決定要重新導向,以及做為重新導向目標中,選取哪一個執行個體的邏輯是整個應用程式定義。

針對 XAML 應用程式中,Main 方法是通常自動產生及隱藏從開發人員。「 重新導向與多重執行個體 」 範本中,會隱藏此行為。如果您更新現有的應用程式,您可以隱藏預設主 DISABLE_XAML_GENERATED_MAIN 加入應用程式的建置屬性中的條件式編譯符號清單。某些應用程式類型 — 例如,c + + DirectX 的應用程式 — 不隱藏的 Main 函式。除了,DirectX 應用程式使用新的應用程式開發介面會遵循 XAML 範例所示相同的模式。

請注意,應用程式只能使用 GetActivatedEventArgs 和 RedirectActivationTo 方法期間 Main;如果在這些呼叫任何其他地方,它們將會失敗。這是因為如果您想要參與啟動過程重新導向,您需要執行此動作非常早期的生命週期的應用程式的過程,和當然任何之前,先建立 windows。

相反地,您可以隨時使用其餘的 AppInstance 方法和屬性。特別是,您可以使用 FindOrRegisterInstanceForKey 每當您需要更新目前的執行個體的索引鍵。例如,如果您的金鑰根據檔案名稱,而您稍後可以關閉此檔案,您會在該時間更新金鑰註冊。取消註冊方法也可用來取消註冊完全如果因故您不想再參與啟動過程重新導向此特定執行個體。此外,任何時候,您可以使用 AppInstance.GetInstances 方法以取得您的應用程式,包括其索引鍵,因此可以理解其狀態的所有已註冊的執行個體的清單。

其他考量事項

多重執行個體的主要增強功能,並初始版本涵蓋主要的案例。具體來說,支援為包含多個執行個體會前景應用程式、 主控台應用程式,且大部分的跨處理序背景工作,包括應用程式服務。不過,沒有支援這個版本中針對 ApplicationTrigger 工作或所有的同處理序背景工作。

在開發期間,Microsoft 會花費相當長的時間,測試各種現有的市集應用程式,以查看如何會產生多個執行個體時執行。從這裡開始,Microsoft 學到的應用程式分成三大類:

  • 因此不是多執行個體的應用程式。這些應用程式就不會選擇功能。
  • 想要多-執行個體,並繼續正常運作,不需要變更任何程式碼的應用程式。只要這些應用程式可以選擇加入多個執行個體,並呼叫完成工作。
  • 想要建立多,但需要執行的工作來允許執行模型中差異的應用程式。

常見的問題,第三個類別目錄中的應用程式是他們所使用的某些中央資源-可能是快取,或資料庫或其他檔案 — 和當單一執行個體它們已安全地假設應用程式有此資源的獨佔存取權。一旦他們選擇在多重執行個體時,可能會嘗試存取資源的多個執行個體。在此案例中,應用程式需要執行的工作來同步處理存取、 鎖定的讀取和寫入,並依此類推,換句話說,一般的同步處理需要的問題傳統 Win32 應用程式考量。

明顯的範例,請考慮使用應用程式的本機存放裝置。這是資源的範例會以封裝為基礎,而不處理程序為基礎上受到限制存取,而且當然應用程式的所有執行個體共用相同的封裝。雖然個別的處理序執行的應用程式的每個執行個體時,它們會都使用相同的本機儲存體和設定,所表示的 ApplicationData.Current API。如果您將本機儲存體中執行資料存取作業,您應該考慮如何有效防止衝突。其中一個選項是使用執行個體唯一的檔案,其中一個執行個體的作業不能在與任何其他的衝突。或者,如果您想要跨多個執行個體使用的一般檔案,您應該鎖定,並適當地解除鎖定檔案的存取權。您可以使用標準機制,例如具名 Mutex 這個。

主控台 UWP 應用程式

在 UWP 環境中的另一個明顯間距是建立遠端控制的主控台應用程式的能力。在 Win32 和其他環境中,您可以建立一個用於輸入和輸出的主控台視窗的命令列工具。因此,我們會將這項支援加入也。同樣地,新的 Visual Studio 專案範本,並如同多執行個體的應用程式,這會產生資訊清單的其他項目。這項功能也會限制到桌面與 IoT,因為只有這些 Sku 實際上有主控台視窗現在不小。相同的 XML 命名空間宣告。< a p > 項目包括 SupportsMultipleInstances 和子系統的屬性,子系統設定設為 「 主控台 」。主控台應用程式必須是多重執行個體,這是傳統的 Win32 主控台應用程式從應用程式的預期的模型。此外,應用程式包含 AppExecutionAlias — 這也有新的子系統屬性中所示,圖 3

圖 3 的其他資訊清單項目為主控台應用程式

<Application Id="App"
  Executable="$targetnametoken$.exe"
  EntryPoint="App9.App"
  desktop4:Subsystem="console"
  desktop4:SupportsMultipleInstances="true"
  iot2:Subsystem="console"
  iot2:SupportsMultipleInstances="true">
...
  <Extensions>
    <uap5:Extension
      Category="windows.appExecutionAlias"
      Executable="App9.exe"
      EntryPoint="App9.App">
      <uap5:AppExecutionAlias
         desktop4:Subsystem="console" 
        iot2:Subsystem="console">
        <uap5:ExecutionAlias Alias="App9.exe"/>
      </uap5:AppExecutionAlias>
    </uap5:Extension>
  </Extensions>
</Application>

您可以變更的別名值,為適用於您的應用程式的項目。同樣地,使用多個執行個體產生程式碼包括 Program.cs 或 Program.cpp 檔案。產生的程式碼中的 c + + 範例所示,提供的方式,您可以實作必要的 main 函式,範例圖 4。您可以將 main 內所有的程式碼取代您自己的自訂程式碼。

圖 4 主控台應用程式的 Main 函式的範本產生程式碼

int __cdecl main()
{
  // You can get parsed command-line arguments from the CRT globals.
  wprintf(L"Parsed command-line arguments:\n");
  for (int i = 0; i < __argc; i++)
  {
    wprintf(L"__argv[%d] = %S\n", i, __argv[i]);
  }
  wprintf(L"Press Enter to continue:");
  getchar();
}

建置並部署應用程式之後,您可以從執行一般的命令提示字元、 PowerShell 視窗中或 Windows-R,如下所示圖 5。請注意,由於應用程式會使用 [主控台] 視窗,預期會有未建立任何其他視窗,事實上,這是不受支援。相反地,應用程式現在可以使用所有 System.Console Api,再加上許多傳統的 Win32 Api 現在已加入至核准清單是特別為了支援主控台應用程式。

從命令列執行主控台 UWP 應用程式
圖 5 從命令列執行主控台 UWP 應用程式

利用此功能,您可以最後建置命令列主控台應用程式,利用 UWP,包括 APPX 封裝的優點、 儲存發行集,輕鬆更新,依此類推。

更廣泛的檔案系統存取

到目前為止,UWP 應用程式只已經可以存取某些特定的資料夾,例如圖片庫以及音樂媒體櫃,然後才在應用程式會宣告為其資訊清單中的功能。此外,應用程式取得存取檔案系統中的任何其他地方的引發 FilePicker 對話方塊,提示使用者選擇的位置,這會授與應用程式權限。

現在,加入 Win32 同位檢查的第三個主要功能會增加 UWP 應用程式的檔案系統存取的層的級。這樣做有兩種,包括:

  • 隱含的存取權的目前工作目錄。
  • 廣泛的檔案系統存取會受限的功能。

從命令列啟動時,任何 UWP 應用程式 (一般的視窗型應用程式或主控台應用程式) 會宣告 AppExecutionAlias 現在會授與隱含的存取權的檔案和資料夾在目前工作目錄,然後向下拖曳。目前工作目錄是在使用者選擇執行您 AppExecutionAlias 從檔案系統位置。這已 debated 長的時間,因為 UWP 模型一直都相當地謹慎授與應用程式的檔案系統存取權。權衡來看,我們決定使用者選擇從特定位置執行應用程式相當於選擇的位置在 FilePicker 對話方塊中,根據授與權限的使用者。

請務必注意應用程式有完全相同檔案的權限的使用者身分執行應用程式,可能仍有檔案或資料夾,讓應用程式無法存取,使用者就無法存取它們,可能是因為。例如,如果使用者無法看到隱藏的檔案,當它們執行 dir 命令時,應用程式也將無法以查看該隱藏的檔案。

若要利用這項功能,您可以撰寫程式碼來尋找 CommandLineActivatedEventArgs OnActivated 覆寫。這包括 CurrentDirectoryPath,在此情況下供使用者執行您 AppExecutionAlias 檔案系統位置。圖 6顯示的範例; 這裡,應用程式會擷取目前的目錄,並將它傳遞至 MainPage。

圖 6 OnActivated 覆寫命令列啟動

protected override void OnActivated(IActivatedEventArgs args)
{
  switch (args.Kind)
  {
    case ActivationKind.CommandLineLaunch:
      CommandLineActivatedEventArgs cmdLineArgs =
         args as CommandLineActivatedEventArgs;
      CommandLineActivationOperation operation = cmdLineArgs.Operation;
      string activationPath = operation.CurrentDirectoryPath;
      Frame rootFrame = Window.Current.Content as Frame;
      if (rootFrame == null)
      {
        rootFrame = new Frame();
        Window.Current.Content = rootFrame;
      }
      rootFrame.Navigate(typeof(MainPage), activationPath);
      Window.Current.Activate();
      break;
  }
}

然後,您無法編碼 MainPage OnNavigatedTo 覆寫從內送 NavigationEventArgs,擷取這個路徑中所示圖 7。在此範例中,正在初始化 StorageFolder 這個路徑中,從應用程式,並將其再建立的檔案和資料夾從這裡向下的樹狀檢視控制項中。

圖 7,建立從目前工作目錄的檔案系統樹狀結構

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
  string activationPath = e.Parameter as string;
  argumentsText.Text = activationPath;
  fileTreeView.RootNodes.Clear();
  try
  {
    StorageFolder folder =
       await StorageFolder.GetFolderFromPathAsync(activationPath);
    if (folder != null)
    {
      TreeViewNode rootNode = new TreeViewNode() { Content = folder.Name };
      IReadOnlyList<StorageFolder> folders = await folder.GetFoldersAsync();
      GetDirectories(folders, rootNode);
      fileTreeView.RootNodes.Add(rootNode);
    }
  }
  catch (Exception ex)
  {
    Debug.WriteLine(ex.Message);
  }
}

新功能

提供更多的檔案系統存取的第二個方式是透過新受限的功能。若要使用這種情況,您必須 restrictedcapabilities XML 命名空間宣告在您的應用程式資訊清單的頂端,並包含 broadFileSystemAccess < 功能 > 清單中:

xmlns:rescap="https://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp uap5 rescap">
...
  <Capabilities>
    <rescap:Capability Name="broadFileSystemAccess" />
  </Capabilities>

如果您宣告任何受限的功能,這會觸發其他監督提交您的封裝,發行集的儲存區時。如果應用程式授與這項功能,它必須在檔案系統與使用者執行應用程式相同的存取。不只是從目前的工作目錄,但使用者可存取的每個地方。如果您有這項功能,您不需要 AppExecutionAlias。因為這是此類功能強大的功能,Microsoft 將會授與功能只当應用程式開發人員提供的要求和描述這個的使用方式,這有益於使用者的方式說明重要的理由。

如果您宣告 broadFileSystemAccess 功能,您不需要宣告的任何更狹窄的檔案系統功能 (文件、 圖片或視訊)。事實上,應用程式不可宣告 broadFileSystemAccess 和任何其他三個檔案系統功能。

即使應用程式已被授與功能之後,另外還有執行階段檢查,因為這會構成使用者的隱私權問題。就像其他隱私權的問題,應用程式將會觸發使用者同意提示第一次使用。如果使用者選擇拒絕權限,必須復原至這個應用程式。使用者也可以變更她注意在任何時候,將相關的檔案系統頁面,在設定中,在 [隱私權] 清單中所示圖 8

新檔案系統] 頁面設定
圖 8 新檔案系統] 頁面設定

請注意,若要充分利用的目前工作目錄的存取和 broadFileSystemAccess 權限,您的程式碼必須用於 WinRT Windows.Storage Api 檔案處理。

總結

以 UWP 的長期策略是關閉與舊版的應用程式技術間距 — 尤其是 Win32 — 使 UWP 經過一段時間越來越多不同的應用程式類型是可行的選項。與支援,則為 true 多重執行個體、 主控台 UWP 應用程式,以及更廣泛的檔案系統存取的簡介,三個的多個大型步驟上建立過此作業。範例程式碼將會位於bit.ly/2GtzM3T,您會發現在 Visual Studio 專案範本和bit.ly/2HApmiibit.ly/2FEIAXu


Andrew Whitechapel是 Microsoft Windows 除法負責通用 Windows 平台的應用程式啟用工作流程中的程式管理員。

這點受惠檢閱本文章下列技術專家:Jason Holmes、 Tim Kurtzman、 Anis Mohammed Khaja Mohideen


MSDN Magazine 論壇中的這篇文章的討論