從 C++/WinRT 元件產生 C# 投影,散發為適用於 .NET 應用程式的 NuGet
在本主題中,我們會逐步解說使用 C#/WinRT 從 C++/WinRT Windows 執行階段元件產生 C# .NET 投影 (或 Interop) 組件,並將其散發為 .NET 應用程式的 NuGet 封裝。
.NET 6 及更新版本不再支援使用 Windows 中繼資料 (WinMD) 檔案 (請參閱 .NET已移除內建 WinRT 支援)。 相反地,C#/WinRT 工具可用來產生任何 WinMD 檔案的投影組件,然後可從 .NET 應用程式取用 WinRT 元件。 投影組件也稱為 Interop 組件。 這篇逐步解說展示如何執行下列動作:
- 使用 C#/WinRT 封裝從 C++/WinRT 元件產生 C# 投影。
- 將元件連同投影組件一起散發為 NuGet 封裝。
- 從 .NET 主控台應用程式取用 NuGet 封裝。
必要條件
這篇逐步解說及相應範例需要下列工具和元件:
- 已安裝通用 Windows 平台開發工作負載的 Visual Studio 2022 (或 Visual Studio 2019)。 在 [安裝詳細資料]>[通用 Windows 平台開發] 中,勾選 [C++ (v14x) 通用 Windows 平台工具] 選項。
- .NET 6.0 SDK 或更新版本。
僅限 Visual Studio 2019。 C++/WinRT VSIX 延伸模組,其提供 Visual Studio 中的 C++/WinRT 專案範本。 專案範本內建於 Visual Studio 2022。
我們將在本逐步解說中使用 Visual Studio 2022 和 .NET 6。
重要
此外,您必須從 GitHub 上的 C#/WinRT 投影範例下載或複製本主題的範例程式碼。 請前往 CsWinRT,然後按一下綠色的 [程式碼] 按鈕以取得git clone
網址。 請務必讀取範例的 README.md 檔案。
建立簡單的 C++/WinRT Windows 執行階段元件
若要依此逐步解說操作,您必須先有 C++/WinRT Windows 執行階段元件 (WRC),才能從中產生 C# 投影組件。
本逐步解說會使用 SimpleMathComponent WRC,其來自於您從 GitHub 下載或複製的 C#/WinRT 投影範例。 SimpleMathComponent 是從 Windows 執行階段元件 (C++/WinRT) Visual Studio 專案範本(隨附於 Visual Studio 2022 或 C++/WinRT VSIX 延伸模組)所建立的專案。
若要在 Visual Studio 中開啟 SimpleMathComponent 專案,請開啟位於您已下載或複製存放庫中的 \CsWinRT\src\Samples\NetProjectionSample\CppWinRTComponentProjectionSample.sln
檔案。
此專案中的程式碼提供基本數學運算功能,如下列標頭檔案所示。
// SimpleMath.h
...
namespace winrt::SimpleMathComponent::implementation
{
struct SimpleMath: SimpleMathT<SimpleMath>
{
SimpleMath() = default;
double add(double firstNumber, double secondNumber);
double subtract(double firstNumber, double secondNumber);
double multiply(double firstNumber, double secondNumber);
double divide(double firstNumber, double secondNumber);
};
}
您可以確認 SimpleMathComponent C++/WinRT Windows 執行階段元件專案的 Windows 桌面相容屬性已設定為 [是]。 若要這樣做,請在 SimpleMathComponent的專案屬性中,於 [組態屬性>一般>專案預設值] 底下,將 [Windows 桌面相容] 屬性設定為[是]。 這可確保載入正確的執行階段二進位檔,以供取用 .NET 傳統型應用程式。
如需建立 C++/WinRT 元件和產生 WinMD 檔案的詳細步驟,請參閱使用 C++/WinRT 的 Windows 執行階段元件。
注意
若要在元件中實作 IInspectable::GetRuntimeClassName,則其必須傳回有效的 WinRT 類別名稱。 因為 C#/WinRT 使用 Interop 的類別名稱字串,所以不正確的執行階段類別名稱會引發 InvalidCastException。
將投影專案新增至元件解決方案
首先,在 Visual Studio 中 CppWinRTComponentProjectionSample 解決方案仍開啟的狀況下,從該解決方案中移除 SimpleMathProjection 專案。 然後,從檔案系統中刪除 SimpleMathProjection 資料夾 (或視需要重新命名)。 必須執行這些步驟,您才可以依照本逐步解說進行操作。
將新的 C# 程式庫專案新增至您的解決方案。
- 在 [方案總管] 中,在解決方案節點上按一下滑鼠右鍵,然後選取 [新增]>[新增專案]。
- 在 [[新增專案] 對話方塊的搜尋方塊中輸入類別庫。 從語言清單中選擇 C#,然後從平台清單中選擇 Windows。 選擇單純名為類別庫的 C# 專案範本 (不含首碼或尾碼),然後按一下 [下一步]。
- 將新專案命名為 SimpleMathProjection。 其位置應已設定在與
\CsWinRT\src\Samples\NetProjectionSample
SimpleMathComponent 資料夾所在相同的資料夾,但仍需加以確認。 然後按一下 [下一步]。 - 在 [其他資訊] 頁面上,選取 [.NET 6.0 (長期支援) ],然後選擇 [建立]。
從專案中刪除虛設常式 Class1.cs 檔案。
使用下列步驟來安裝 C#/WinRT NuGet 封裝。
- 在 [方案總管] 中,以滑鼠右鍵按一下您的 SimpleMathProjection 專案,然後選取 [管理 NuGet 封裝]。
- 在 [瀏覽] 索引標籤中的搜尋方塊中輸入或貼上 Microsoft.Windows.CsWinRT,從搜尋結果中選取具有最新版本的專案,然後按一下 [安裝],將封裝安裝到 SimpleMathProjection 專案中。
為 SimpleMathProjection 新增以 SimpleMathComponent 專案為對象的專案參考。 在 [方案總管]中,以滑鼠右鍵按一下 [SimpleMathProjection] 專案節點底下的 [相依性] 節點,選取 [新增專案參考],然後選取 [SimpleMathComponent 專案 >[確定]。
請勿嘗試建置專案。 我們將在稍後的步驟中執行此動作。
截至目前,您的方案總管外觀應該像這樣 (您的版本編號將會不同)。
在來源外部建置專案
對於 C#/WinRT 投影範例中的 CppWinRTComponentProjectionSample 解決方案(您已從 GitHub 下載或複製且現已開啟),其建置輸出位置是使用 Directory.Build.props 檔案設定為在來源外部進行建置。 這表示建置輸出中的檔案會產生於來源資料夾外部。 在使用 C#/WinRT 工具時,建議您在來源外部進行建置。 這可防止 C# 編譯器意外挑選專案根目錄下的所有 *.cs 檔案,這可能會導致重複的類型錯誤 (例如編譯多個組態和/或平台時)。
即使 CppWinRTComponentProjectionSample 解決方案已採用此設定,仍請依下列步驟練習自行設定。
要將解決方案設定為在來源外部建置:
在 CppWinRTComponentProjectionSample 解決方案仍開啟的狀況下,以滑鼠右鍵按一下解決方案節點,然後選取 [新增] > [新專案]。 選取 XML 檔案 項目,將其命名為 Directory.Build.props (不含
.xml
副檔名)。 按一下 [是] 覆寫現有檔案。以下列組態取代 Directory.Build.props 的內容。
<Project> <PropertyGroup> <BuildOutDir>$([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))</BuildOutDir> <OutDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))</OutDir> <IntDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))</IntDir> </PropertyGroup> </Project>
儲存並關閉 Directory.Build.props 檔案。
編輯專案檔以執行 C#/WinRT
您必須先編輯專案檔以指定若干專案屬性,才能叫用 cswinrt.exe
工具來產生投影組件。
在 方案總管]中,按兩下 SimpleMathProjection 節點,以在編輯器中開啟專案檔。
更新
TargetFramework
元素,將目標設為特定的 Windows SDK 版本, 如此可新增 Interop 和投影支援所需的組件相依性。 此範例的目標是 Windows SDK 版本 net6.0-windows10.0.19041.0 (又稱 Windows 10 版本 2004)。 將Platform
元素設定為 AnyCPU,以便可以從任何應用程式架構參考所產生的投影組件。 您也可以設定TargetPlatformMinimumVersion
屬性,以允許參考應用程式支援舊版 Windows SDK。<PropertyGroup> <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework> <!-- Set Platform to AnyCPU to allow consumption of the projection assembly from any architecture. --> <Platform>AnyCPU</Platform> </PropertyGroup>
注意
在本篇逐步解說和相關範例程式碼中,此解決方案是針對 x64 和 Release 所組建。 請注意,SimpleMathProjection 專案經過設定,會建置所有解決方案架構組態的 AnyCPU。
新增第二個
PropertyGroup
元素 (緊接在第一個之後),其會設定數個 C#/WinRT 屬性。<PropertyGroup> <CsWinRTIncludes>SimpleMathComponent</CsWinRTIncludes> <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir> </PropertyGroup>
以下是此範例中設定的一些詳細資料:
CsWinRTIncludes
屬性會指定要投影的命名空間。CsWinRTGeneratedFilesDir
屬性會設定輸出目錄,以在其中產生投影來源檔案。 此屬性設定為OutDir
,定義於上一節的 Directory.Build.props 中。
儲存並關閉 SimpleMathProjection.csproj 檔案,然後視需要按一下以重新載入專案。
使用投影建立 NuGet 封裝
若要散發 .NET 應用程式開發人員的投影組件,您可以新增一些額外專案屬性,以在建置解決方案時自動建立 NuGet 封裝。 對於 .NET 目標,NuGet 封裝必須包含來自元件的投影組件和實作組件。
使用下列步驟,將 NuGet 規格 (
.nuspec
) 檔案新增至 SimpleMathProjection 專案。- 在 [方案總管] 中,以滑鼠右鍵按一下 [SimpleMathProjection] 節點,選擇 [新增] > [新資料夾],並將資料夾命名為 nuget。
- 以滑鼠右鍵按一下 [nuget] 資料夾,選擇 [[新增]>[新專案],選擇 [XML 檔案],將其命名為 SimpleMathProjection.nuspec。
在 方案總管]中,按兩下 SimpleMathProjection 節點,以在編輯器中開啟專案檔。 將下列屬性群組新增至目前開啟的 SimpleMathProjection.csproj (緊接在兩項現有的
PropertyGroup
元素之後) 以自動產生封裝。 這些屬性會指定要產生 NuGet 封裝的NuspecFile
和目錄。<PropertyGroup> <GeneratedNugetDir>.\nuget\</GeneratedNugetDir> <NuspecFile>$(GeneratedNugetDir)SimpleMathProjection.nuspec</NuspecFile> <OutputPath>$(GeneratedNugetDir)</OutputPath> <GeneratePackageOnBuild>true</GeneratePackageOnBuild> </PropertyGroup>
注意
如需個別產生封裝,您也可以選擇從命令列執行
nuget.exe
工具。 如需建立 NuGet 封裝的詳細資訊,請參閱 使用 nuget.exe CLI 建立封裝。開啟 SimpleMathProjection.nuspec 檔案來編輯封裝建立屬性,並貼上下列程式碼。 下列程式碼片段是將 SimpleMathComponent 散發至多個目標架構的 NuGet 規格範例。 請注意,對於目標
lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll
,指定的是專案組件 SimpleMathProjection.dll 而不是simpleMathComponent.winmd。 此行為是 .NET 6 及更新版本中的新功能,且由 C#/WinRT 啟用。 實作元件SimpleMathComponent.dll
也必須散發,而且會在執行階段載入。<?xml version="1.0" encoding="utf-8"?> <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> <metadata> <id>SimpleMathComponent</id> <version>0.1.0-prerelease</version> <authors>Contoso Math Inc.</authors> <description>A simple component with basic math operations</description> <dependencies> <group targetFramework="net6.0-windows10.0.19041.0" /> <group targetFramework=".NETCoreApp3.0" /> <group targetFramework="UAP10.0" /> <group targetFramework=".NETFramework4.6" /> </dependencies> </metadata> <files> <!--Support .NET 6, .NET Core 3, UAP, .NET Framework 4.6, C++ --> <!--Architecture-neutral assemblies--> <file src="..\..\_build\AnyCPU\Release\SimpleMathProjection\bin\SimpleMathProjection.dll" target="lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll" /> <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\netcoreapp3.0\SimpleMathComponent.winmd" /> <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\uap10.0\SimpleMathComponent.winmd" /> <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\net46\SimpleMathComponent.winmd" /> <!--Architecture-specific implementation DLLs should be copied into RID-relative folders--> <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x64\native\SimpleMathComponent.dll" /> <!--To support x86 and Arm64, build SimpleMathComponent for those other architectures and uncomment the entries below.--> <!--<file src="..\..\_build\Win32\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x86\native\SimpleMathComponent.dll" />--> <!--<file src="..\..\_build\arm64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-arm64\native\SimpleMathComponent.dll" />--> </files> </package>
注意
SimpleMathComponent.dll 是元件的實作組件,且專屬於特定架構。 如果您要支援其他平台 (例如 x86 或 Arm64),則必須先建置所需平台的 SimpleMathComponent,然後將這些組件檔案新增至適當的 RID 相對資料夾。 投影組件 SimpleMathProjection.dll 和元件 simpleMathComponent.winmd 皆為架構中性。
儲存並關閉您剛才編輯的檔案。
建置解決方案以產生投影和 NuGet 封裝
在建置解決方案之前,請務必檢查 Visual Studio 中的 Configuration Manager 設定,其位於 [建置]>[Configuration Manager] 底下。 本篇逐步解說,將解決方案的組態設定為 Release,並將其平台設定為 x64。
此時,您已可以建置解決方案。 以滑鼠右鍵按一下您的解決方案節點,然後選取 [建置解決方案]。 此步驟會先建置 SimpleMathComponent 專案,然後是 SimpleMathProjection 專案。 元件 WinMD 和實作組件 (SimpleMathComponent.winmd 和 SimpleMathComponent.dll)、投影來源檔案和投影組件 (SimpleMathProjection.dll) 都會產生於 _build 輸出目錄下。 您也可以在 \SimpleMathProjection\nuget 資料夾下看到所產生的 NuGet 封裝 SimpleMathComponent0.1.0-prerelease.nupkg。
重要
如果未產生上述任何檔案,請再次建置解決方案。 在重建之前,您可能也需要關閉並重新開啟解決方案。
您可能需要關閉並重新開啟解決方案,.nupkg
才會如圖所示出現在 Visual Studio 中 (或是直接選取 [顯示所有檔案] 後隨即取消選取)。
參考 C# .NET 6 主控台應用程式中的 NuGet 封裝
若要從 .NET 專案取用 SimpleMathComponent,只需為新的 .NET 專案新增一項參考,而該參考的對象即是我們在上一節建立的 SimpleMathComponent0.1.0-prerelease.nupkg NuGet 封裝。 下列步驟示範如何在個別解決方案中建立簡單的主控台應用程式來執行此動作。
使用下列步驟來建立一項包含 C# 主控台應用程式專案的新解決方案 (在新解決方案中建立此專案,可讓您獨立還原 SimpleMathComponent NuGet 封裝)。
重要
我們將在
\CsWinRT\src\Samples\NetProjectionSample
資料夾中建立這個新的主控台應用程式專案,其位於您所下載或複製的 C#/WinRT 投影範例中。- 在 Visual Studio 的新執行個體中,選取 [檔案]>[新增]>[專案]。
- 在 [建立新專案] 對話方塊中,搜尋 [主控台應用程式] 專案範本。 選擇單純名為主控台應用程式的 C# 專案範本 (不含首碼或尾碼),然後按一下 [下一步]。 如果使用 Visual Studio 2019,則專案範本會是 主控台應用程式。
- 將新專案命名為 SampleConsoleApp,將其位置設定為與 SimpleMathComponent 和 SimpleMathProjection 所在的相同資料夾
\CsWinRT\src\Samples\NetProjectionSample
中,然後按一下 [下一步]。 - 在 [其他資訊] 頁面上,選取 [.NET 6.0 (長期支援) ],然後選擇 [建立]。
在 [方案總管]中按兩下 SampleConsoleApp 節點以開啟 SampleConsoleApp.csproj 專案檔,並編輯
TargetFramework
和Platform
屬性,使其外觀如下列清單所示。 如果沒有,請新增Platform
元素。<PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework> <Platform>x64</Platform> </PropertyGroup>
接下來,在 SampleConsoleApp.csproj 專案檔仍開啟的狀況下,為 SampleConsoleApp 專案新增以 SimpleMathComponent NuGet 封裝為對象的參考。 若要在建置專案時還原 SimpleMathComponent NuGet,您可以使用
RestoreSources
屬性搭配元件解決方案中 nuget 的資料夾路徑。 複製下列組態,將其貼入 sampleConsoleApp.csproj (位於Project
元素內)。<PropertyGroup> <RestoreSources> https://api.nuget.org/v3/index.json; ../SimpleMathProjection/nuget </RestoreSources> </PropertyGroup> <ItemGroup> <PackageReference Include="SimpleMathComponent" Version="0.1.0-prerelease" /> </ItemGroup>
重要
上述 SimpleMathComponent 封裝的
RestoreSources
路徑會設定為../SimpleMathProjection/nuget
。 如果您遵循本逐步解說中的步驟,則此路徑應該正確無誤,因此 SimpleMathComponent 和 SampleConsoleApp 專案都位於相同的資料夾中 (在此例中為NetProjectionSample
資料夾)。 如果您已執行不同操作,則必須據以調整該路徑。 或者,您也可以將本機 NuGet 封裝摘要新增至您的解決方案。編輯 Program.cs 檔案以便使用 SimpleMathComponent 提供的功能。
var x = new SimpleMathComponent.SimpleMath(); Console.WriteLine("Adding 5.5 + 6.5 ..."); Console.WriteLine(x.add(5.5, 6.5).ToString());
儲存並關閉您剛才編輯的檔案,然後建置並執行主控台應用程式。 您應該會看到下列輸出。
已知問題
- 建置投影專案時,您可能會看到下列錯誤:錯誤MSB3271 正在建置的專案「MSIL」的處理器架構不符合「..\SimpleMathComponent.winmd」的實作檔案「..\SimpleMathComponent.dll」之處理器架構「x86」。此不符合的情況可能導致執行階段失敗。請考慮透過 Configuration Manager 變更專案的目標處理器架構,使專案與實作檔之間的處理器架構相對應,或選擇內含的實作檔具有符合專案目標處理器架構的 winmd 檔案。若要解決此問題,請將下列屬性新增至您的 C# 程式庫專案檔:
<PropertyGroup> <!-- Workaround for MSB3271 error on processor architecture mismatch --> <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> </PropertyGroup>
進一步考量
我們在本主題中示範建立的 C# 投影 (或 Interop) 組件相當簡單,與其他元件不具相依性。 但對於具有 Windows App SDK 類型參考的 C++/WinRT 元件,若要產生 C# 投影,則必須在投影專案中新增對 Windows App SDK NuGet 封裝的參考。 如果遺漏任何這類參考,您會看到諸如「找不到類型 <T> 」的錯誤。
在本主題中執行的另一件事是將投影散發為 NuGet 封裝。 這目前是必要步驟。