開始使用 C++/WinRT

這很重要

關於如何設定 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 是一個範圍,由 beginend 函式(或其 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 撰寫的範例應用程式

請參閱 哪裡可以找到 C++/WinRT 範例應用程式?

重要 API