這很重要
關於如何設定 Visual Studio 以支援 C++/WinRT 開發的資訊——包括安裝及使用 C++/WinRT Visual Studio 擴充功能(VSIX)和 NuGet 套件(兩者共同提供專案範本與建置支援)——請參閱 Visual Studio 對 C++/WinRT 的支援。
為了讓你快速掌握使用 C++/WinRT,本主題將帶你參考一個基於新 Windows 主控台應用程式(C++/WinRT)專案的簡單程式碼範例。 本主題也說明如何在 Windows 桌面應用程式專案中加入 C++/WinRT 支援。
Note
我們建議您使用最新版本的 Visual Studio 和 Windows SDK,但如果您使用 Visual Studio 2017(版本 15.8.0 或更新版本),並目標為 Windows SDK 版本 10.0.17134.0(Windows 10),版本 1803),然後新建立的 C++/WinRT 專案可能會因錯誤「錯誤 C3861: 'from_abi': identifier not found」而無法編譯,並出現其他源自 base.h 的錯誤。 解決方案是針對較後期(更符合規範)的 Windows SDK 版本,或設定專案屬性 C/C++>Language>相容模式:否(另外,若 /permissive- 出現在專案屬性 C/C++>Language>命令列的附加選項中,則刪除該模式)。
C++/WinRT 快速入門工具
建立一個新的 Windows 主控台應用程式(C++/WinRT)專案。
編輯 pch.h 後, main.cpp 看起來像這樣。
// pch.h
#pragma once
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
#include <iostream>
// main.cpp
#include "pch.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
int main()
{
winrt::init_apartment();
Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;
syndicationClient.SetRequestHeader(L"User-Agent", L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
for (const SyndicationItem syndicationItem : syndicationFeed.Items())
{
winrt::hstring titleAsHstring = syndicationItem.Title().Text();
// A workaround to remove the trademark symbol from the title string, because it causes issues in this case.
std::wstring titleAsStdWstring{ titleAsHstring.c_str() };
titleAsStdWstring.erase(remove(titleAsStdWstring.begin(), titleAsStdWstring.end(), L'™'), titleAsStdWstring.end());
titleAsHstring = titleAsStdWstring;
std::wcout << titleAsHstring.c_str() << std::endl;
}
}
讓我們逐段說明上述簡短程式碼,說明每個部分的內容。
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
在預設專案設定中,所包含的標頭來自 Windows SDK %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt資料夾 內的 。 Visual Studio 在其 IncludePath 巨集中包含該路徑。 但對 Windows SDK 沒有嚴格依賴,因為你的專案(透過工具cppwinrt.exe)會將相同的標頭產生到專案的 $(GeneratedFilesDir) 資料夾中。 如果找不到,或你更改專案設定,檔案會從該資料夾載入。
這些標頭檔包含經由 C++/WinRT 投影的 Windows API。 換句話說,對於每種 Windows 類型,C++/WinRT 都定義了一個對 C++ 友善的對應型態(稱為投影型別)。 投影型別具有與 Windows 類型相同的完整限定名稱,但它位於 C++ winrt 命名空間中。 將這些 #include 指示詞放入預先編譯標頭中,可以縮短增量建置所需的時間。
這很重要
每當你想使用來自 Windows 命名空間的型別時,必須#include使用對應的 C++/WinRT Windows 命名空間標頭檔,如上所示。
對應的標頭與該型別命名空間名稱相同。 例如,若要使用 C++/WinRT 投影來處理 Windows::Foundation::Collections::P ropertySet 執行時類別,請包含winrt/Windows.Foundation.Collections.h標頭。
C++/WinRT 投影標頭通常會自動包含相關的命名空間標頭檔案。 例如, winrt/Windows.Foundation.Collections.h 包含 winrt/Windows.Foundation.h。 但你不應該依賴這種行為,因為它是會隨時間改變的實作細節。 你必須明確包含你需要的任何標頭。
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;
這些 using namespace 指令是可選的,但很方便。 上面顯示的這類指令模式(允許對 winrt 命名空間中的任何項目進行未限定的名稱查詢)適用於你開始新專案時,且該專案中唯一使用的語言投影是 C++/WinRT。 另一方面,如果你是混合 C++/WinRT 程式碼與 C++/CX 及/或 SDK 應用程式二進位介面(ABI)程式碼(你是從其中一個或兩個模型移植或互通),那就看 C++/WinRT 與 C++/CX 之間的互通性、 從 C++/CX 轉到 C++/WinRT,以及 C++/WinRT 與 ABI 之間的互通性主題。
winrt::init_apartment();
呼叫 winrt::init_apartment 會初始化 Windows 執行階段 中的執行緒;預設情況下,在多執行緒公寓中。 通話同時初始化 COM。
Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;
堆疊配置兩個物件:它們代表 Windows 部落格的 URI,以及一個聯發客戶端。 我們用一個簡單的寬字串字面來建構 URI(更多字串處理方式,請參見 C++/WinRT 中的字串處理 )。
SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
SyndicationClient::RetrieveFeedAsync 是非同步 Windows 執行階段 函式的範例。 程式碼範例會從 RetrieveFeedAsync 接收一個非同步操作物件,並呼叫該物件的 get 來阻擋呼叫執行緒並等待結果(此處為聯發訊號)。 欲了解更多關於並行性及非阻塞技術,請參見 C++/WinRT 的並行與非同步操作。
for (const SyndicationItem syndicationItem : syndicationFeed.Items()) { ... }
SyndicationFeed.Items 是一個範圍,由 begin 與 end 函式(或其 const、reverse 與 const-reverse 版本)所傳回的迭代器界定。 因此,你可以用基於for範圍的陳述式,或使用 std::for_each 模板函式來枚舉項目。 每當你像這樣遍歷 Windows 執行階段 集合時,都需要 #include <winrt/Windows.Foundation.Collections.h>。
winrt::hstring titleAsHstring = syndicationItem.Title().Text();
// Omitted: there's a little bit of extra work here to remove the trademark symbol from the title text.
std::wcout << titleAsHstring.c_str() << std::endl;
取得訂閱源的標題文字,作為 winrt::hstring 物件(更多細節可參考 C++/WinRT 的字串處理)。 接著透過 c_str 函式輸出 hstring,這反映了 C++ 標準函式庫字串所採用的模式。
如你所見,C++/WinRT 鼓勵現代且類類的 C++ 表達式,例如 syndicationItem.Title().Text()。 這是一種與傳統 COM 節目不同且更乾淨的節目風格。 你不需要直接初始化 COM,也不需要使用 COM 指標。
你也不需要處理 HRESULT 回傳代碼。 C++/WinRT 將錯誤 HRESULTS 轉換為例外,例如 winrt::hresult-error ,以呈現自然且現代的程式設計風格。 關於錯誤處理及程式碼範例的更多資訊,請參見 C++/WinRT 的錯誤處理。
修改 Windows 桌面應用程式專案以加入 C++/WinRT 支援
部分桌面專案(例如 Visual Studio 中的 WinUI 3 範本)內建了 C++/WinRT 支援。
但這部分會告訴你如何為任何 Windows 桌面應用程式專案加入 C++/WinRT 支援。 如果你沒有現有的 Windows 桌面應用程式專案,可以先依照這些步驟建立一個。 例如,打開Visual Studio並建立一個Visual C++>Windows桌面>Windows桌面應用程式專案。
你可以選擇安裝 C++/WinRT Visual Studio 擴充功能(VSIX)和 NuGet 套件。 詳情請參閱 Visual Studio 對 C++/WinRT 的支援。
設定專案屬性
前往專案屬性 GeneralWindows>SDK 版本,選擇「所有配置與所有平台」。 請確保 Windows SDK 版本設定為 10.0.17134.0(Windows 10,版本 1803)或更高版本。
確認你沒有受到 「為什麼我的新專案無法編譯?」的影響。
由於 C++/WinRT 使用 C++17 標準的功能,請將專案屬性 C/C++>Language>Language Standard 設定為 ISO C++17 標準(/std:c++17)。
預編譯標頭
預設的專案範本會為你建立一個預先編譯好的標頭,名稱為 framework.h、 或 stdafx.h。 把它重新命名為 pch.h。 如果你有一個 stdafx.cpp 檔案,那就將其重新命名為 pch.cpp。 將專案屬性 C/C++>預先編譯標頭>預先編譯標頭 設為 建立 (/Yc),並將 預先編譯標頭檔 設為 pch.h。
尋找並替換所有 #include "framework.h" (或 #include "stdafx.h")為 #include "pch.h"。
在 pch.h中,包含 winrt/base.h。
// pch.h
...
#include <winrt/base.h>
連結
C++/WinRT 語言投影取決於某些 Windows 執行階段 自由函式(非成員函式)以及需要連結至 WindowsApp.lib 傘狀程式庫的進入點。 本節描述滿足連結器的三種方式。
第一個選項是將所有 C++/WinRT MSBuild 屬性和目標加入你的 Visual Studio 專案。 要這麼做,請在你的專案中安裝 Microsoft.Windows.CppWinRT NuGet 套件。 在 Visual Studio 中開啟該專案,按一下 Project>Manage NuGet Packages...>Browse,在搜尋方塊中輸入或貼上 Microsoft.Windows.CppWinRT,選取搜尋結果中的項目,然後按一下 Install,即可為該專案安裝套件。
你也可以使用專案連結設定來明確連結 WindowsApp.lib。 或者,你也可以像這樣在原始碼中操作(例如在 pch.h 中)。
#pragma comment(lib, "windowsapp")
你現在可以編譯、連結,並將 C++/WinRT 程式碼加入你的專案(例如,類似上述 A C++/WinRT 快速入門 章節所示的程式碼)。
C++/WinRT 的三個主要情境
當你使用並熟悉 C++/WinRT,並閱讀這裡的其他文件時,你很可能會注意到有三個主要情境,詳見以下章節。
使用 Windows API 和型別
換句話說,就是 使用或 呼叫 API。 例如,透過藍牙呼叫 API 來通訊;串流並呈現影片;與 Windows shell 整合;等等。 C++/WinRT 完全且毫不妥協地支援這類情境。 更多資訊請參閱 Consume API with C++/WinRT。
撰寫 Windows API 與類型
換句話說,就是 產生 API 和型別。 例如,產生上述節中描述的 API;或圖形 API;儲存與檔案系統 API;網路 API 等等。 更多資訊請參閱 C++/WinRT 的作者 API。
用 C++/WinRT 撰寫 API 比直接使用它們複雜一些,因為你必須先用 IDL 定義 API 的形狀,才能實作。 在 XAML 控制項;繫結至 C++/WinRT 屬性 中有關於如何進行該操作的逐步解說。
XAML 應用程式
這個情境是關於在 XAML UI 框架上建置應用程式和控制項。 在 XAML 應用程式中工作,實際上是消耗與撰寫的結合。 但由於 XAML 是目前 Windows 上主導的 UI 框架,且它對 Windows 執行階段 的影響與此成正比,因此它理應擁有一個獨立的情境類別。
請注意,XAML 最適合用於提供反射功能的程式語言。 在 C++/WinRT 中,有時你得多做一點工作才能與 XAML 框架互通。 這些案例都在文件中都有涵蓋。 好的起點是 XAML 控制項;綁定到 C++/WinRT 屬性 ,並用 C++/WinRT 綁定 XAML 自訂(模板化)控制項。
以 C++/WinRT 撰寫的範例應用程式
重要 API
- SyndicationClient::RetrieveFeedAsync 方法
- SyndicationFeed.Items 屬性
- winrt::hstring struct
- winrt::hresult-error struct