在傳統型應用程式中呼叫 Windows 執行階段 API

本主題描述如何設定傳統型應用程式專案,以使用 Windows OS 所提供的 Windows 執行階段 (WinRT) API,以及將新式 Windows 11 和 Windows 10 體驗新增至傳統型應用程式。

傳統型應用程式中不支援部分 Windows 執行階段 (WinRT) API。 如需詳細資訊,請參閱傳統型應用程式中不支援 Windows 執行階段 API

修改 .NET 專案以使用 Windows 執行階段 API

.NET 專案有數個選項:

  • 從 .NET 6 開始,您可以在專案檔中指定目標 Framework Moniker (TFM) 以存取 WinRT API。 將 Windows 10 版本 1809 或更新版本設為目標的專案支援此選項。
  • 若為舊版 .NET,您可以安裝 Microsoft.Windows.SDK.Contracts NuGet 套件,以將所有必要參考新增至您的專案。 將 Windows 10 版本 1803 或更新版本設為目標的專案支援此選項。
  • 如果您的專案將 .NET 6 (或更新版本) 和舊版 .NET 設為多重目標,則您可以將專案檔設定為使用這兩個選項。

.NET 6 和更新版本:使用目標 Framework Moniker 選項

僅使用 .NET 6 (或更新版本) 以及將 Windows 10 版本 1809 或更新作業系統版本設為目標的專案支援此選項。 藉由在專案檔中指定 Windows OS 版本特定的 TFM,參考便會新增至適當的 Windows SDK 目標套件。 如需更多關於此案例的背景資訊,請參閱部落格文章在 .NET 中呼叫 Windows API

  1. 在 Visual Studio 中開啟您的專案,在 [方案總管] 中您的專案上按一下滑鼠右鍵,然後選擇 [編輯專案檔]。 您的專案檔看起來應該類似這樣。

    注意

    下列範例顯示 WinExeOutputType,其會指定 Windows GUI 可執行檔 (並防止在應用程式執行時開啟主控台視窗)。 如果您的應用程式沒有 GUI,則您的 OutputType 會有不同的值。 您可以從 Windows GUI 應用程式、主控台應用程式和程式庫呼叫 WinRT API。 此外,您 TargetFramework 的值可能不符合下列範例。

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
      </PropertyGroup>
    </Project>
    
  2. 保留所有其他設定,將 TargetFramework 元素的值取代為下列其中一個字串:

    • net6.0-windows10.0.17763.0:如果您的應用程式將 Windows 10 版本 1809 設為目標,請使用此值。
    • net6.0-windows10.0.18362.0:如果您的應用程式將 Windows 10 版本 1903 設為目標,請使用此值。
    • net6.0-windows10.0.19041.0:如果您的應用程式將 Windows 10 版本 2004 設為目標,請使用此值。
    • net6.0-windows10.0.22000.0:如果您的應用程式將 Windows 11 設為目標。

    例如,下列元素適用於將 Windows 10 版本 2004 設為目標的專案。

    <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
    

    在更新版本的 .NET 中,您可以將值取代為相關的版本,例如 net6.0-windows10.0.19041.0

  3. 儲存您的變更並關閉專案檔。

.NET 6 或更新版本不支援 WinRT API

在 .NET 6 和更新版本中,Windows.UI 命名空間中有數個不支援的 Windows 執行階段 (WinRT) API。 針對下面所列的 API,WinUI (Microsoft.UI) 命名空間中存在對等版本的 API (例如 Microsoft.UI.Text)。 .NET 6 和更新版本不支援下列 WinRT API:

支援多個 Windows OS 版本

Windows OS 版本特定的 TargetFramework 屬性會決定編譯您應用程式的 Windows SDK 版本。 這個屬性會決定建置時間的可存取 API 集合,並提供 TargetPlatformVersionTargetPlatformMinVersion 的預設值 (如果未明確設定)。 TargetPlatformVersion 屬性不需要在專案檔中明確定義,因為其由 TargetFramework OS 版本自動設定。

TargetPlatformMinVersion 可以覆寫為小於 TargetPlatformVersion (由 TargetFramework 屬性中的版本決定)。 這可讓應用程式在舊版 OS 上執行。 例如,您可以在專案檔中進行下列設定,以讓應用程式向下支援 Windows 10 版本 1809。

<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
   <OutputType>WinExe</OutputType>
   <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
   <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
 </PropertyGroup>
</Project>

請注意,將 TargetPlatformMinVersion 設定為低於 TargetPlatformVersion 的版本時,會產生呼叫無法使用 API 的可能性。 呼叫所有支援的 OS 版本上無法使用的 WinRT API 時,建議您使用 ApiInformation 檢查來保護這些呼叫。 如需詳細資訊,請參閱版本設定調適型應用程式

舊版 .NET:安裝 Microsoft.Windows.SDK.Contracts NuGet 套件

如果您的應用程式使用 .NET Core 3.x 或 .NET Framework,請使用此選項。 將 Windows 10 版本 1803 或更新版本設為目標的專案支援此選項。

  1. 請確定已啟用套件參考

    1. 在 Visual Studio 中,按一下 [工具 - >NuGet 套件管理員 - >套件管理員設定]
    2. 確定已針對 [預設套件管理格式] 選取 [PackageReference]
  2. 在 Visual Studio 中開啟您的專案,在 [方案總管] 中您的專案上按一下滑鼠右鍵,然後選擇 [管理 NuGet 套件]

  3. 在 [NuGet 套件管理員] 視窗中,選取 [瀏覽] 索引標籤,然後搜尋 Microsoft.Windows.SDK.Contracts

  4. 找到 Microsoft.Windows.SDK.Contracts 套件之後,請在 [NuGet 套件管理員] 的右窗格中,根據您想要設為目標的 Windows 10 版本,選取您要安裝的套件 [版本]

    • 10.0.19041.xxxx:針對 Windows 10 版本 2004 選擇此版本。
    • 10.0.18362.xxxx:針對 Windows 10 版本 1903 選擇此版本。
    • 10.0.17763.xxxx:針對 Windows 10 版本 1809 選擇此版本。
    • 10.0.17134.xxxx:針對 Windows 10 版本 1803 選擇此版本。
  5. 按一下 [安裝]

設定將不同版本 .NET 設為多重目標的專案

如果您的專案將 .NET 6 (或更新版本) 和舊版 (包括 .NET Core 3.x 和 .NET Framework) 設為多重目標,則您可以將專案檔設定為使用目標 Framework Moniker (TFM),以自動提取 .NET 6 (或更新版本) 的 WinRT API 參考,並使用舊版的 Microsoft.Windows.SDK.Contracts NuGet 套件。

  1. 在 Visual Studio 中開啟您的專案,在 [方案總管] 中您的專案上按一下滑鼠右鍵,然後選擇 [編輯專案檔]。 下列範例示範應用程式的專案檔,該應用程式使用 .NET Core 3.1。

    注意

    下列範例顯示 WinExeOutputType,其會指定 Windows GUI 可執行檔 (並防止在應用程式執行時開啟主控台視窗)。 如果您的應用程式沒有 GUI,則您的 OutputType 會有不同的值。 您可以從 Windows GUI 應用程式、主控台應用程式和程式庫呼叫 WinRT API。 此外,您 TargetFramework 的值可能不符合下列範例。

    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWindowsForms>true</UseWindowsForms>
      </PropertyGroup>
    </Project>
    
  2. 將檔案中的 TargetFramework 元素取代為 TargetFrameworks 元素 (請注意複數)。 在此元素中,為您想要設為目標的所有 .NET 版本指定目標 Framework Moniker (TFM),以分號區隔。

    • 若為 .NET 6 或更新版本,請使用下列其中一個目標 Framework Moniker (TFM):
      • net6.0-windows10.0.17763.0:如果您的應用程式將 Windows 10 版本 1809 設為目標,請使用此值。
      • net6.0-windows10.0.18362.0:如果您的應用程式將 Windows 10 版本 1903 設為目標,請使用此值。
      • net6.0-windows10.0.19041.0:如果您的應用程式將 Windows 10 版本 2004 設為目標,請使用此值。
    • 若為 .NET Core 3.x,請使用 netcoreapp3.0netcoreapp3.1
    • 若為 .NET Framework,請使用 net46

    下列範例示範如何將 .NET Core 3.1 和 .NET 6 (適用於 Windows 10 版本 2004) 設為多重目標。

    <TargetFrameworks>netcoreapp3.1;net6.0-windows10.0.19041.0</TargetFrameworks>
    
  3. PropertyGroup 元素後面,新增 PackageReference 專案,其中包含的條件陳述式會針對您應用程式設為目標的任何 .NET Core 3.x 或 .NET Framework 版本安裝 Microsoft.Windows.SDK.Contracts NuGet 套件。 PackageReference 元素必須是 ItemGroup 元素的子項。 以下範例示範 .NET Core 3.1 的操作方法。

    <ItemGroup>
      <PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
                        Include="Microsoft.Windows.SDK.Contracts"
                        Version="10.0.19041.0" />
    </ItemGroup>
    

    完成時,您的專案檔看起來應該類似這樣。

    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFrameworks>netcoreapp3.1;net6.0-windows10.0.19041.0</TargetFrameworks>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
      <ItemGroup>
        <PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
                         Include="Microsoft.Windows.SDK.Contracts"
                         Version="10.0.19041.0" />
      </ItemGroup>
    </Project>
    
  4. 儲存您的變更並關閉專案檔。

修改 C++ 桌面 (Win32) 專案以使用 Windows 執行階段 API

使用 C++/WinRT 來取用 WinRT API。 C++/WinRT 是完全標準現代的 WinRT API 的 C++17 語言投影,僅實作為標頭檔案式程式庫,以及設計用來提供您現代化 Windows API 的第一級存取。

設定專案使其適用於 C++/WinRT:

如需關於這些選項的更多詳細資料,請參閱 C++/WinRT 的 Visual Studio 支援,以及 VSIX

新增 Windows 10 體驗

現在,您可以新增當使用者在 Windows 10 上執行您的應用程式時提供的現代化體驗了。 請使用以下設計流程。

首先,決定您想要新增的體驗

您有許多選擇。 例如,您可以使用創造營收 API 簡化採購訂單流程,或透過分享一些有趣事物來吸引別人關注您的應用程式,例如另一位使用者張貼的新圖片。

Toast notification

即使使用者忽略或關閉您的訊息,他們仍可以在控制中心再次看到這些訊息,按一下即可開啟您的應用程式。 如此可提高使用者與您應用程式的互動程度,並獲得您的應用程式與作業系統深度整合的額外好處。 本文稍後會顯示該體驗的程式碼。

請造訪 UWP 文件看看更多的構想。

決定要增強或擴充

您會經常聽到我們使用「增強」、「擴充」這些詞彙,所以我們想花一點時間來解釋這些詞彙到底是什麼意思。

我們會使用「增強」一詞來描述您可以直接從傳統型應用程式呼叫的 WinRT API,無論其是否為已封裝的應用程式。 當您選擇 Windows 10 體驗後,找出您需要建立的 API,然後查看該 API 是否出現在此清單中。 這個清單列出您可以直接從您的傳統型應用程式呼叫的 API。 如果您的 API 未顯示在此清單中,這是因為與該 API 相關的功能只能在 UWP 程序中執行。 很多時候,這包括轉譯 UWP XAML 的 API,例如 UWP 地圖控制項或 Windows Hello 安全性提示。

注意

雖然轉譯 UWP XAML 的 API 通常無法直接從您的傳統型應用程式呼叫,但您還是可以使用替代做法。 如果您想要裝載 UWP XAML 控制項或其他自訂視覺效果體驗,可以使用 XAML Islands (從 Windows 10 版本1903 開始) 和視覺圖層 (從 Windows 10 版本 1803 開始)。 這些功能可以在已封裝或未封裝的傳統型應用程式中使用。

如果您已選擇封裝您的傳統型應用程式,另一個選項是將 UWP 專案新增至您的方案,藉此「擴充」應用程式。 桌面專案仍是您應用程式的進入點,但 UWP 專案可讓您存取未出現在此清單中的所有 API。 傳統型應用程式可以透過使用應用程式服務來與 UWP 程序通訊,而且我們提供大量關於如何設定的指導方針。 如果您想要新增需要 UWP 專案的體驗,請參閱透過 UWP 元件擴充

參考 API 協定

如果您可以直接從傳統型應用程式呼叫 API,請開啟瀏覽器並搜尋該 API 的參考主題。 在 API 摘要的下方,您可以找到描述其 API 協定的表格。 以下是表格範例:

API contract table

如果您有 .NET 傳統型應用程式,請新增指向該 API 協定的參考,然後將該檔案的 [複製本機] 屬性設定為 [False]。 如果您有 C++ 專案,請將包含此協定之資料夾的路徑新增至 [其他 Include 目錄]

呼叫 API 以新增您的體驗

以下是我們之前討論過的,您要用來顯示通知視窗的程式碼。 這些 API 出現在此清單中,因此您可以將此程式碼新增到您的傳統型應用程式並立即執行。

using Windows.Foundation;
using Windows.System;
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;
...

private void ShowToast()
{
    string title = "featured picture of the day";
    string content = "beautiful scenery";
    string image = "https://picsum.photos/360/180?image=104";
    string logo = "https://picsum.photos/64?image=883";

    string xmlString =
    $@"<toast><visual>
       <binding template='ToastGeneric'>
       <text>{title}</text>
       <text>{content}</text>
       <image src='{image}'/>
       <image src='{logo}' placement='appLogoOverride' hint-crop='circle'/>
       </binding>
      </visual></toast>";

    XmlDocument toastXml = new XmlDocument();
    toastXml.LoadXml(xmlString);

    ToastNotification toast = new ToastNotification(toastXml);

    ToastNotificationManager.CreateToastNotifier().Show(toast);
}
#include <sstream>
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.UI.Notifications.h>

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Notifications;
using namespace winrt::Windows::Data::Xml::Dom;

void UWP::ShowToast()
{
    std::wstring const title = L"featured picture of the day";
    std::wstring const content = L"beautiful scenery";
    std::wstring const image = L"https://picsum.photos/360/180?image=104";
    std::wstring const logo = L"https://picsum.photos/64?image=883";

    std::wostringstream xmlString;
    xmlString << L"<toast><visual><binding template='ToastGeneric'>" <<
        L"<text>" << title << L"</text>" <<
        L"<text>" << content << L"</text>" <<
        L"<image src='" << image << L"'/>" <<
        L"<image src='" << logo << L"'" <<
        L" placement='appLogoOverride' hint-crop='circle'/>" <<
        L"</binding></visual></toast>";

    XmlDocument toastXml;

    toastXml.LoadXml(xmlString.str().c_str());

    ToastNotificationManager::CreateToastNotifier().Show(ToastNotification(toastXml));
}
using namespace Windows::Foundation;
using namespace Windows::System;
using namespace Windows::UI::Notifications;
using namespace Windows::Data::Xml::Dom;

void UWP::ShowToast()
{
	Platform::String ^title = "featured picture of the day";
	Platform::String ^content = "beautiful scenery";
	Platform::String ^image = "https://picsum.photos/360/180?image=104";
	Platform::String ^logo = "https://picsum.photos/64?image=883";

	Platform::String ^xmlString =
		L"<toast><visual><binding template='ToastGeneric'>" +
		L"<text>" + title + "</text>" +
		L"<text>"+ content + "</text>" +
		L"<image src='" + image + "'/>" +
		L"<image src='" + logo + "'" +
		L" placement='appLogoOverride' hint-crop='circle'/>" +
		L"</binding></visual></toast>";

	XmlDocument ^toastXml = ref new XmlDocument();

	toastXml->LoadXml(xmlString);

	ToastNotificationManager::CreateToastNotifier()->Show(ref new ToastNotification(toastXml));
}

若要深入了解通知,請參閱調適型和互動式快顯通知

支援 Windows XP、Windows Vista 和 Windows 7/8 安裝基礎

您不需要建立新的分支並維護個別程式碼基底,即可針對 Windows 10 現代化您的應用程式。

如果您想為 Windows 10 使用者建置不同的二進位檔,請使用條件式編譯。 如果您傾向組建一組二進位檔然後部署到所有 Windows 使用者,請使用執行階段檢查。

讓我們快速看過每個選項。

條件式編譯

您可以保留一個程式碼基底,並針對 Windows 10 使用者編譯一組二進位檔。

首先,將新的組建設定新增到您的專案。

Build Configuration

對於該組建設定,請建立常數,用來識別呼叫 WinRT API 的程式碼。

對於 .NET 型專案,此常數稱為條件式編譯常數

Conditional Compilation constant

對於 C++ 型專案,此常數稱為前置處理器定義

Preprocessor Definition constant

將該常數新增至任何 UWP 程式碼區塊之前。

[System.Diagnostics.Conditional("_UWP")]
private void ShowToast()
{
 ...
}
#if _UWP
void UWP::ShowToast()
{
 ...
}
#endif

只有當您的使用中組建設定已定義該常數時,編譯器才會組建該程式碼。

執行階段檢查

您可以為所有 Windows 使用者編譯一組二進位檔,不考慮他們執行什麼 Windows 版本。 只有當使用者以 Windows 10 上的封裝應用程式執行您的應用程式時,您的應用程式才會呼叫 WinRT API。

將執行階段檢查新增至程式碼最簡單的方式是安裝此 Nuget 套件:傳統型橋接器協助程式,然後使用 IsRunningAsUWP() 方法來關閉呼叫 WinRT API 的所有程式碼。 如需更多詳細資料,請參閱此部落格文章:傳統型橋接器 - 識別應用程式的內容

尋找您的問題解答

有任何疑問嗎? 請在 Stack Overflow 上發問。 我們的團隊會監視這些標籤。 您也可以在我們的論壇上提出詢問。