2017 年 1 月

第 32 卷,第 1 期

本文章是由機器翻譯。

C++ - 介紹 C++/WinRT

Kenny Kerr |2017 年 1 月

Windows 執行階段 (WinRT) 是最新的 Windows API 和核心的通用 Windows 平台 (UWP) 背後的技術。相同的 API 可跨所有 Windows 裝置,無論是平板電腦、 HoloLens、 電話、 Xbox 或其他任何執行 Windows 的電腦。

WinRT 為基礎的元件物件模型 (COM),但其 COM Api,其設計並非直接使用,因為您可能使用傳統的 COM Api,例如 DirectX 中。相反地,它被設計來透過所謂的 「 語言推測 」。 語言投影封裝 COM Api 所使用的不愉快的詳細資訊,並提供特定的程式設計語言更自然的程式設計經驗。例如,WinRT Api 可從 JavaScript 和.NET 相當自然,而不需要太擔心 COM 支援。

直至近日之前,沒有很好的語言投影的 c + + 開發人員使用︰ 您必須選擇笨拙的 C + + /CX 語言擴充功能或詳細資訊,十分繁瑣和複雜 WinRT c + + 樣板程式庫 (WRL)。

這是 C + /cli WinRT 進來。C + + /cli WinRT 是標準的 c + + 語言投影 WinRT 完全實作於標頭檔,最佳的 c + + 程式庫類型。它被設計來支援撰寫和使用任何標準的 c + + 編譯器的 WinRT Api 的耗用量。C + + /cli WinRT 最後為第一級存取 c + + 開發人員提供最新的 Windows 應用程式開發介面。

Windows 中繼資料檔案放在拯救

C + + /cli WinRT 根據現代 c + + Windows 執行階段專案 (moderncpp.com),我在加入 Microsoft 之前啟動的專案。它會依據另一個專案中建立嘗試現代化 DirectX 程式設計 (dx.codeplex.com)。WinRT 出現之後,當它解決 No.1 現代化 COM api 提供標準的方式來說明 API 介面,透過所謂的 Windows 中繼資料 (.winmd) 檔案。一組指定的 Windows 中繼資料檔案,C + + /cli WinRT 編譯器 (cppwinrt.exe) 可以產生標準的 c + + 程式庫的完整描述,或專案的 Windows API 或其他 WinRT 元件,讓開發人員可以同時使用並產生該 API 的實作。後者非常重要,因為這表示,C + + /cli WinRT 不只是進行呼叫,或使用 WinRT Api,但它也適用於實作 WinRT 元件。Microsoft 內部團隊已經開始使用 C + + /cli WinRT 建置 WinRT 元件本身的作業系統。

C + + /cli WinRT 有發行之初公用 GitHub 上年 10 月 2016,因此,您可以立即試用。可能是最簡單的選項是複製 git 儲存機制中,雖然您也可以下載它為.zip 檔案。只要發出下列命令︰

git clone https://github.com/Microsoft/cppwinrt.git

Cloning into 'cppwinrt'..

您現在應該有一個名為 「 cppwinrt 」,其中包含您自己的本機複本在儲存機制的資料夾。此資料夾包含 C + + /cli WinRT 程式庫,以及一些說明文件和取得入門指南。在程式庫本身會包含在反映出針對其建置的 Windows sdk 版本的資料夾。撰寫本文時,最新版本為 10.0.14393.0,也就是支援開發 Windows 10 週年紀念日更新 (RS1) 的 Windows SDK 組建。您的本機資料夾看起來可能像這樣︰

dir /b cppwinrt

10.0.14393.0
Docs
Getting Started.md
license.txt
media
README.md

版本控制資料夾本身包含兩個資料夾︰

dir /b cppwinrt\10.0.14393.0

Samples
winrt

Winrt 資料夾是您所需。如果您只想要將該資料夾複製到您自己的專案或原始檔控制系統,這是最適合的。它包含所有必要的標頭,因此您可以快速開始撰寫程式碼。該程式碼的外觀為何? 告訴你吧,讓我們開始您可以只編譯使用 Visual c + + 工具命令提示字元的主控台應用程式。Visual Studio 或不需要複雜.msbuild 或.appx 檔案裝訂 UWP 應用程式一般。程式碼複製**[圖 1**到來源檔案,並將它命名為 Feed.cpp。

[圖 1] 您第一次的 C + + /cli WinRT 應用程式

#pragma comment(lib, "windowsapp")
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Web.Syndication.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
int main()
{
  initialize();
  Uri uri(L"http://kennykerr.ca/feed");
  SyndicationClient client;
  SyndicationFeed feed = client.RetrieveFeedAsync(uri).get();
  for (SyndicationItem item : feed.Items())
  {
    hstring title = item.Title().Text();
    printf("%ls\n", title.c_str());
  }
}

我將討論了解程式碼的表示。現在,您可以確定組建下列 Visual c + + 編譯器選項︰

cl Feed.cpp /I cppwinrt\10.0.14393.0 /EHsc /std:c++latest

Microsoft (R) C/C++ Optimizing Compiler...

/Std:c + + 最新的編譯器旗標是解決方案的線索,您將需要 Visual c + + 2015 Update 3 或更新版本使用 C + + /cli WinRT。不過,請注意,是沒有所要求的 Microsoft 專有擴充功能。事實上,您也可以包含進一步限制使程式碼更符合標準 /permissive-option。如果一切順利,編譯器會將連結器,並可執行檔呼叫 Feed.exe 印出我近期的部落格文章的標題一起產生︰

Feed.exe

C++/WinRT: Consumption and Production
C++/WinRT: Getting Started
Getting Started with Modules in C++
...

在 [顯示與

恭喜您! 您已經建置第一個應用程式使用 C + + /cli WinRT。因此,什麼呢? 看一下**[圖 1**。第一行是告訴連結尋找少數幾個使用的任何語言投影的 WinRT 函式的位置。這些都不是特定 C + /cli WinRT。它們只是 Windows Api 可讓應用程式或元件初始化執行緒的 apartment 內容、 操作 WinRT 字串、 啟動 factory 物件、 傳播錯誤資訊,依此類推。

接下來是一組的 #include 指示詞來包含該程式會使用命名空間的型別。最初,C + + /cli WinRT 定義單一標頭檔中的所有內容,但提供的 Windows API 十足的大小,這證明不利建置輸送量。在未來,我們可能會改為使用 c + + 模組我述我 2016 年 「 Microsoft 推播 c + + 到未來 」 的 MSDN Magazine 文章 (msdn.com/magazine/mt694085)。該日時,必須先包含這些標頭可能不再需要因為模組格式更有效率,而且初期的試用版已表示組建輸送量問題主要是可能會消失。

使用命名空間指示詞是選擇性,但假設 Windows API 相當大量使用命名空間,執行會相當實用。所有型別,以及其封入命名空間裝置在 C + + /cli 呼叫 winrt WinRT 根命名空間。這是有點太妙,但需要交互操作性與現有程式碼︰ 這兩個 C + + /CX 和 Windows SDK 宣告型別在根命名空間中名為 「 Windows 」。 具有不同的命名空間可讓開發人員緩時變移轉遠離 C + + /cli CX 同時保留現有的投資這些較舊的技術。

應用程式在**[圖 1**只會建立表示若要下載的 RSS 摘要的 Uri 物件。Windows.Foundation 命名空間中定義的 Uri 物件。此 Uri 是將物件傳遞至 SyndicationClient 物件來擷取摘要的方法。SyndicationClient Windows.Web.Syndication 命名空間中定義。如您稍後所見,WinRT 高度依賴非同步,因此應用程式必須等候 RetrieveFeedAsync 方法的結果,而這行尾的 get 方法的工作。SyndicationFeed 物件之後,與摘要的項目可能表示列舉透過項目方法所傳回的集合。每個產生的 SyndicationItem 可能再解除封裝到文章的標題中擷取文字。結果會是類型 hstring 封裝 WinRT HSTRING 類型中,但提供的 c + + 開發人員提供熟悉的體驗都類似的介面。

如您所見,WinRT 的目標是透過嘗試與測試 COM 配管的許多 Windows 作業系統核心呈現 classy 型別系統。C + + /cli WinRT 準確地接受該理想。就不需要呼叫艱澀難懂的函式,例如 CoCreateInstanceEx 或處理 COM 指標或 HRESULT 錯誤碼。當然,道德相當於這些 COM 樣式的基本項目仍會存在幕後。例如,initialize 函式呼叫的主函式的開頭**[圖 1**在內部呼叫 RoInitialize 函式,相當於 WinRT 的傳統的 CoInitializeEx 函數,從傳統 com 使用。C + + /cli WinRT 程式庫也會負責 Hresult 和例外狀況,以自然且高生產力的程式設計模型提供開發人員間的轉換。這是以避免程式碼膨脹,並改善 inlineability 的方式。

WinRT 以 async 具有深度架構投資,而且您看到這已經在範例中提到**[圖 1**。RetrieveFeedAsync 函式本質上可能需要一些時間才能完成,而 API 設計人員不想封鎖,該方法呼叫,而不會傳回 SyndicationFeed 直接它改為傳回 IAsyncOperationWithProgress 的型別,表示可能最後會導致 SyndicationFeed 最後可以取得這些結果時的作業。

並行處理

WinRT 提供四種類型來代表不同種類的非同步處理物件和 C + + /cli WinRT 提供數種方式來建立並使用這類物件。[圖 1示範如何封鎖呼叫執行緒,直到結果可供使用,但 C + + /cli WinRT 也整合 c + + 共同常式很深的程式設計模型,提供自然的方式來合作等候結果,而不妨礙 OS 執行緒從其他有用的工作。而不是使用 get 方法,您可以撰寫 co_await 陳述式,並且等候作業的結果,如下所示︰

SyndicationFeed feed = co_await client.RetrieveFeedAsync(uri);

您也可以產生您自己的 WinRT 非同步物件,只要撰寫 coroutine 中所示**[圖 2**。產生的 IAsyncAction,另一個 Windows.Foundation 命名空間中找到的 WinRT 非同步型別接著會彙總成其他共同常式、 呼叫端可能會決定使用 get 方法來封鎖與等候結果,或甚至可以傳遞至另一種支援 WinRT 的程式設計語言。

[圖 2 共同常式使用 C + + /cli WinRT

IAsyncAction PrintFeedAsync()
{
  Uri uri(L"http://kennykerr.ca/feed");
  SyndicationClient client;
  SyndicationFeed feed = co_await client.RetrieveFeedAsync(uri);
  for (SyndicationItem item : feed.Items())
  {
    hstring title = item.Title().Text();
    printf("%ls\n", title.c_str());
  }
}
int main()
{
    initialize();
    PrintFeedAsync().get();
}

前面提過,C + + /cli WinRT 幾乎沒有呼叫 WinRT Api。它是一樣的容易實作 WinRT 介面而產生的實作,其他人可能會呼叫。WinRT 應用程式模型的結構的方式,是一個很好的範例。最小的應用程式看起來可能像這樣︰

using namespace Windows::ApplicationModel::Core;
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  IFrameworkViewSource source = ...
  CoreApplication::Run(source);
}

這只是傳統 WinMain 進入點函式提醒您,即使您現在可以撰寫標準 c + + 中,您仍然在特定平台上撰寫更亮眼的應用程式的商務。CoreApplication 靜態 Run 方法預期 IFrameworkViewSource 物件來建立應用程式的第一個檢視。IFrameworkViewSource 是一個介面具有單一方法和查詢,至少在概念上,像這樣︰

struct IFrameworkViewSource : IInspectable
{
  IFrameworkView CreateView();
};

因此,假設 IFrameworkViewSource,您可能會呼叫它,如下所示︰

IFrameworkViewSource source = ...
IFrameworkView view = source.CreateView();

當然,它不是應用程式開發人員會呼叫它,但作業系統。與您的應用程式開發人員應該實作這個介面,以及傳回 CreateView IFrameworkView 介面。C + + /cli WinRT 可以讓您輕而易舉若要這樣做。同樣地,沒有必要的 COM 樣式程式設計。您不需要求助於嚇人 WRL 或 ATL 樣板和巨集。您可以直接使用 C + + /cli WinRT 程式庫實作介面,如下所示︰

struct Source : implements<Source, IFrameworkViewSource>
{
  IFrameworkView CreateView()
  {
    return ...
  }
};

實作類別樣板是鬆散根據 variadic 範本的方法實作 WinRT 介面。我會說明這在我 2014年特殊 connect ();文件 「 Visual c + + 2015年將現代化 c + + 以 Windows API 」 (msdn.com/magazine/dn879346)。

第一個型別參數是打算實作做為後續的型別參數所列出的任何介面衍生類別的名稱。您要如何實作 IFrameworkView 介面? 沒錯,另一個介面,只是稍微複雜一點。它看起來像這樣︰

struct IFrameworkView : IInspectable
{
  void Initialize(CoreApplicationView const & applicationView);
  void SetWindow(Windows::UI::Core::CoreWindow const & window);
  void Load(hstring_ref entryPoint);
  void Run();
  void Uninitialize();
};

提供 IFrameworkView 的執行個體,您可以自由地呼叫這些方法就像它們所述,但一次指應用程式預期來實作,而且作業系統會呼叫的介面。同樣地,您可以直接使用 C + + /cli 來實作此介面中所示的 WinRT [圖 3。這表示將會出現一個視窗上的最小應用程式,並在桌面上執行甚至時並不會令人興奮的任何項目。我告訴您這不是要說明如何撰寫下一個絕佳的應用程式,但說明如何使用自然的 c + + 程式碼使用及產生 WinRT 類型。

[圖 3 實作 IFrameworkView

struct View : implements<View, IFrameworkView>
{
  void Initialize(CoreApplicationView const & view)
  {
  }
  void Load(hstring_ref entryPoint)
  {
  }
  void Uninitialize()
  {
  }
  void Run()
  {
    CoreWindow window = CoreWindow::GetForCurrentThread();
    window.Activate();
    CoreDispatcher dispatcher = window.Dispatcher();
    dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
  }
  void SetWindow(CoreWindow const & window)
  {
    // Prepare app visuals here
  }
};

您可以接著更新 IFrameworkViewSource 實作,並傳回這項實作︰

struct Source : implements<Source, IFrameworkViewSource>
{
  IFrameworkView CreateView()
  {
    return make<View>();
  }
};

同樣地,您可以更新您的 WinMain 函式,讓使用 IFrameworkViewSource 實作的如下所示︰

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
  CoreApplication::Run(make<Source>());
}

假設 variadic 樣板是實作在類別樣板,它可用來輕鬆地實作多個介面。中所示,您可能決定在一個類別中實作 IFrameworkViewSource 和 IFrameworkView [圖 4

[圖 4 實作多個介面使用 C + + /cli WinRT

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
  // IFrameworkViewSource method...
  IFrameworkView CreateView()
  {
    return *this;
  }
  // IFrameworkView methods...
  void Initialize(CoreApplicationView const & view);
  void Load(hstring_ref entryPoint);
  void Uninitialize();
  void Run();
  void SetWindow(CoreWindow const & window);
};

如果您曾經有實作使用 ATL 或 WRL 之類的程式庫的 COM 物件,這應該是一大改進。它不只更方便、 更有效率且更有趣若要使用,而且事實上提供您最小的二進位檔和可能的最佳效能的任何 WinRT 語言投影。它甚至會勝過手寫的程式碼直接使用 ABI 介面。這是因為抽象概念設計為利用現代 c + + 語言的 Visual c + + 編譯器設計用來最佳化。這包括 magic 靜態變數、 空的基底類別、 strlen elision 以及許多較新的最佳化在最新版本的 Visual c + + 適用對象特別提升效能的 C + + /cli WinRT。

例如,WinRT 引進了所需的介面的概念。指定執行階段類別或介面可能必須另外實作某些其他組的介面。這可讓 WinRT,根據以往無法變更,支援版本控制的 COM 介面。例如,稍早所述的 Uri 類別,則需要 IStringable 介面使用其單一的 ToString 方法。一般應用程式開發人員不知道,這個方法的介面實際上由不同的 COM 介面和 vtable 提供,並可讓 IStringable 使用它,如下所示︰

Uri uri(L"http://kennykerr.ca/feed");
hstring value = uri.ToString();

在幕後,C + + /cli WinRT 必須先查詢 IStringable 介面使用 IUnknown QueryInterface 方法的 Uri 物件。這很好以及之前所需的介面方法呼叫在迴圈中會發生此問題︰

Uri uri(L"http://kennykerr.ca/feed");
for (unsigned i = 0; i != 10'000'000; ++i)
{
  uri.ToString();
}

您沒有看到是,此程式碼會導致發生此情形類似︰

Uri uri(L"http://kennykerr.ca/feed");
for (unsigned i = 0; i != 10'000'000; ++i)
{
  uri.as<IStringable>().ToString();
}

C + + /cli WinRT 插入與必要呼叫方法中,依序呼叫 QueryInterface。現在,從 COM 的觀點來看,QueryInterface 是純粹的呼叫。如果一次成功,則它必須永遠成功之特定物件。Visual c + + 編譯器還好,現在我們最佳化來偵測這種模式在共同作業與 C + + /cli WinRT,讓程式碼就變成這樣了,它會找出這個呼叫移至迴圈外︰

Uri uri(L"http://host/path");
IStringable temp = uri.as<IStringable>();
for (unsigned i = 0; i != 10'000'000; ++i)
{
  temp.ToString();
}

這樣會結束上是很重要的最佳化,因為 Windows API 中的必要介面數目是相當顯著,尤其是在 XAML 開發等方面。這只是一個範例的一些驚人的優勢,可用於您為您採用 C + + /cli WinRT 應用程式和元件的開發。有存取啟動的處理站,導致明顯的改進效能的執行個體啟動 (建構函式) 的成本的進一步最佳化和靜態方法呼叫,讓您最多可達 40 x 效能改進透過 C + + /CX。

標準 c + + 會提供一些特殊的問題給任何人嘗試產生 WinRT 語言投影,這有一部分是為什麼在 Microsoft 的 Visual c + + 小組原本想出了非標準的解決方案。工作尚未完成,但我們繼續使用抹黑 C + + /cli 目標是要對系統程式設計師,應用程式開發人員提供絕不妥協第一級語言投影 WinRT — 實際的開發人員想要撰寫適用於 Windows 的美麗又快速的程式碼。

總結

James McNellis,我提供兩個討論在 CppCon 2016,我們正式揭曉的 C + + /cli WinRT。您可以找到這些兩個的討論的影片︰

  • 「 為 Windows 執行階段採行標準 c + + 」 (bit.ly/2ePwbyz)
  • 「 把 Windows 執行階段使用的共同常式 」 (bit.ly/2fMLZqy)

這些是 「 幕後 」 而不是技術簡報。如需高階的介紹,聽聽我 CppCast 訪談,在bit.ly/2fwF6bx

最後,您可以下載並提供 C + + /cli WinRT 試用今天: github.com/microsoft/cppwinrt

很多工作仍然完成且可能尚未有遵循標準 c + + 的演進,並尋找其他方法可改善及簡化的程式設計模型的某些變更。讓我知道您的想法。我希望您的意見反應。


Kenny Kerr是 c + + 系統程式設計師,建立者的 C + + /cli WinRT、 pluralsight,作者和在 Windows 工程小組在 Microsoft。 在他的部落格kennykerr.ca ,您可以關注他的 Twitter: @kennykerr

感謝下列 Microsoft 技術專家來檢閱這份文件︰ James McNellis