diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj
new file mode 100644
index 000000000..4552e80ba
--- /dev/null
+++ b/Benchmarks/Benchmarks.csproj
@@ -0,0 +1,55 @@
+
+
+
+ Exe
+ x64;x86
+ netcoreapp2.0;net5.0;netcoreapp3.1
+ false
+ false
+
+ true
+ Benchmarks.manifest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BenchmarkComponent.dll
+ PreserveNewest
+ True
+
+
+
+ $(MSBuildThisFileDirectory)..\_build\$(Platform)\$(Configuration)\BenchmarkComponent\bin\BenchmarkComponent\BenchmarkComponent.winmd
+ true
+
+
+
+
+ Benchmarks.manifest
+ PreserveNewest
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Benchmarks/Benchmarks.manifest b/Benchmarks/Benchmarks.manifest
new file mode 100644
index 000000000..74774097c
--- /dev/null
+++ b/Benchmarks/Benchmarks.manifest
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/Benchmarks/Program.cs b/Benchmarks/Program.cs
new file mode 100644
index 000000000..89255f080
--- /dev/null
+++ b/Benchmarks/Program.cs
@@ -0,0 +1,95 @@
+using BenchmarkDotNet.Running;
+using System;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Toolchains.CsProj;
+using BenchmarkDotNet.Toolchains.DotNetCli;
+using BenchmarkDotNet.Toolchains;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Characteristics;
+using System.IO;
+
+namespace Benchmarks
+{
+ public class Program
+ {
+ static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new CustomConfig().Config);
+
+ private class CustomConfig : Attribute, IConfigSource
+ {
+ public IConfig Config { get; } = DefaultConfig.Instance;
+
+ public CustomConfig()
+ {
+ // Test CsWinRT projection
+ var job = Job.Default
+ .WithPlatform(BenchmarkDotNet.Environments.Platform.X64)
+ .WithArguments(
+ new Argument[] {
+ new MsBuildArgument("/p:platform=x64")
+ }
+ ).AsDefault();
+
+ // Test WinMD support
+#if NETCOREAPP3_1
+ // BenchmarkDotNet will rebuild the project with a project reference to this project when this project's output exe is ran. It
+ // will be ran from the same folder as where we have the application manifest binplaced which we want to embed in the new exe.
+ string manifestFile = Path.Combine(
+ Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
+ "Benchmarks.manifest");
+
+ var winmdJob = Job.Default
+ .WithPlatform(BenchmarkDotNet.Environments.Platform.X64)
+ .WithToolchain(new NetCore3ToolChainWithNativeExecution())
+ .WithArguments(
+ new Argument[] {
+ new MsBuildArgument("/p:platform=x64"),
+ new MsBuildArgument("/p:ApplicationManifest=" + manifestFile),
+ new MsBuildArgument("/p:BenchmarkWinmdSupport=true")
+ }
+ )
+ .WithId("WinMD NetCoreApp31");
+
+ // Optimizer needs to be diabled as it errors on WinMDs
+ Config = Config.WithOption(ConfigOptions.DisableOptimizationsValidator, true)
+ .AddJob(winmdJob);
+#else
+ Config = Config.AddJob(job);
+#endif
+ }
+ }
+
+ // Custom tool chain for building the benchmark with WinMDs as we need to execute the
+ // exe version of the benchmark rather than the dll version which runs under dotnet cli.
+ // This is because we need to be able to embed a side by side manifest for reg free winrt
+ // and we need COM to be able to find the WinMDs.
+ private class NetCore3ToolChainWithNativeExecution : Toolchain
+ {
+ public NetCore3ToolChainWithNativeExecution()
+ : base("netcoreapp3.1-native",
+ new CsProjGeneratorWithNativeExe(NetCoreAppSettings.NetCoreApp31),
+ CsProjCoreToolchain.NetCoreApp31.Builder,
+ new Executor())
+ {
+ }
+
+ public override bool IsSupported(BenchmarkCase benchmarkCase, ILogger logger, IResolver resolver)
+ {
+ return CsProjCoreToolchain.NetCoreApp31.IsSupported(benchmarkCase, logger, resolver);
+ }
+ }
+
+ private class CsProjGeneratorWithNativeExe : CsProjGenerator
+ {
+ public CsProjGeneratorWithNativeExe(NetCoreAppSettings settings)
+ :base(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath, settings.PackagesPath, settings.RuntimeFrameworkVersion)
+ {
+ }
+
+ protected override string GetExecutableExtension()
+ {
+ return ".exe";
+ }
+ }
+ }
+}
diff --git a/Benchmarks/QueryInterface.cs b/Benchmarks/QueryInterface.cs
new file mode 100644
index 000000000..567ef3aa9
--- /dev/null
+++ b/Benchmarks/QueryInterface.cs
@@ -0,0 +1,79 @@
+using BenchmarkComponent;
+using BenchmarkDotNet.Attributes;
+using Windows.ApplicationModel.Chat;
+
+namespace Benchmarks
+{
+ [MemoryDiagnoser]
+ public class QueryInterfacePerf
+ {
+ ClassWithMultipleInterfaces instance;
+ ChatMessage message;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ instance = new ClassWithMultipleInterfaces();
+ message = new ChatMessage();
+ }
+
+ [Benchmark]
+ public int QueryDefaultInterface()
+ {
+ return instance.DefaultIntProperty;
+ }
+
+ [Benchmark]
+ public int QueryNonDefaultInterface()
+ {
+ return instance.IntProperty;
+ }
+
+ [Benchmark]
+ public bool QueryNonDefaultInterface2()
+ {
+ return instance.BoolProperty;
+ }
+
+ [Benchmark]
+ public void QueryDefaultInterfaceSetProperty()
+ {
+ instance.DefaultIntProperty = 4;
+ }
+
+ [Benchmark]
+ public void QueryNonDefaultInterfaceSetProperty()
+ {
+ instance.IntProperty = 4;
+ }
+
+ [Benchmark]
+ public bool QuerySDKDefaultInterface()
+ {
+ return message.IsForwardingDisabled;
+ }
+
+ [Benchmark]
+ public bool QuerySDKNonDefaultInterface()
+ {
+ return message.IsSeen;
+ }
+
+ // The following 2 benchmarks try to benchmark the time taken for the first call
+ // rather than the mean time over several calls. It has the overhead of the object
+ // construction, but it can be used to track regressions to performance.
+ [Benchmark]
+ public int ConstructAndQueryDefaultInterfaceFirstCall()
+ {
+ ClassWithMultipleInterfaces instance2 = new ClassWithMultipleInterfaces();
+ return instance2.DefaultIntProperty;
+ }
+
+ [Benchmark]
+ public int ConstructAndQueryNonDefaultInterfaceFirstCall()
+ {
+ ClassWithMultipleInterfaces instance2 = new ClassWithMultipleInterfaces();
+ return instance2.IntProperty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 573d6c7eb..f8dd4e42b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -42,6 +42,7 @@
Windows
+ 10.0.18362.0
diff --git a/Projections/Benchmark/Benchmark.csproj b/Projections/Benchmark/Benchmark.csproj
new file mode 100644
index 000000000..02265c124
--- /dev/null
+++ b/Projections/Benchmark/Benchmark.csproj
@@ -0,0 +1,65 @@
+
+
+
+ netstandard2.0;net5.0
+ x64;x86
+ 8
+
+
+
+ true
+ true
+ 8305;0618
+
+
+
+ full
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ high
+ $(GeneratedFilesDir)cswinrt_benchmark.rsp
+ $(CsWinRTExe) %40"$(CsWinRTResponseFile)"
+
+
+
+-verbose
+-in 10.0.18362.0
+-in @(ReferenceWinMDs->'"%(FullPath)"', ' ')
+-out "$(GeneratedFilesDir.TrimEnd('\'))"
+-exclude Windows
+-include BenchmarkComponent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Projections/Test/Test.csproj b/Projections/Test/Test.csproj
index 803485d94..399e05808 100644
--- a/Projections/Test/Test.csproj
+++ b/Projections/Test/Test.csproj
@@ -3,7 +3,6 @@
netstandard2.0;net5.0
x64;x86
- 10.0.18362.0
8
diff --git a/Projections/WinUI/WinUI.csproj b/Projections/WinUI/WinUI.csproj
index 7d072ba81..7d5287727 100644
--- a/Projections/WinUI/WinUI.csproj
+++ b/Projections/WinUI/WinUI.csproj
@@ -3,7 +3,6 @@
netstandard2.0;net5.0
x64;x86
- 10.0.18362.0
8
diff --git a/Projections/Windows/Windows.csproj b/Projections/Windows/Windows.csproj
index fd75e76b9..8d96377ee 100644
--- a/Projections/Windows/Windows.csproj
+++ b/Projections/Windows/Windows.csproj
@@ -3,7 +3,6 @@
netstandard2.0;net5.0
x64;x86
- 10.0.18362.0
8
diff --git a/README.md b/README.md
index 0836f9f0d..f9e6ff868 100644
--- a/README.md
+++ b/README.md
@@ -117,12 +117,16 @@ The **/TestComponentCSharp** folder contains an implementation of a WinRT test c
## /Projections
-The **/Projections** folder contains several projects for generating and building projections from the Windows SDK, WinUI, and Test metadata (produced by the TestWinRT and TestComponentCSharp projects).
+The **/Projections** folder contains several projects for generating and building projections from the Windows SDK, WinUI, Benchmark (produced by the BenchmarkComponent project), and Test metadata (produced by the TestWinRT and TestComponentCSharp projects).
## /UnitTest
The **/UnitTest** folder contains unit tests for validating the Windows SDK, WinUI, and Test projections generated above. All pull requests should ensure that this project executes without errors.
+## /Benchmarks
+
+The **/Benchmarks** folder contains benchmarks written using BenchmarkDotNet to track the performance of scenarios in the generated projection. To run the benchmarks using the CsWinRT projection, run **benchmark.cmd**. To run the same benchmarks using the built-in WinMD support in NET Core 3.1 to compare against as a baseline, run **benchmark_winmd.cmd**.
+
## /WinUIDesktopSample
The **/WinUIDesktopSample** contains an end-to-end sample app that uses the Windows SDK and WinUI projections generated above.
diff --git a/benchmark.cmd b/benchmark.cmd
new file mode 100644
index 000000000..4ebba8bd6
--- /dev/null
+++ b/benchmark.cmd
@@ -0,0 +1,2 @@
+msbuild Benchmarks\Benchmarks.csproj -t:restore -t:build /p:platform=x64 /p:configuration=release
+dotnet %~dp0Benchmarks\bin\x64\Release\netcoreapp2.0\Benchmarks.dll -filter * --runtimes netcoreapp2.0 netcoreapp3.1 netcoreapp5.0
\ No newline at end of file
diff --git a/benchmark_winmd.cmd b/benchmark_winmd.cmd
new file mode 100644
index 000000000..feaa12298
--- /dev/null
+++ b/benchmark_winmd.cmd
@@ -0,0 +1,4 @@
+msbuild Benchmarks\Benchmarks.csproj -t:restore -t:clean;rebuild /p:BenchmarkWinmdSupport=true /p:platform=x64 /p:configuration=release /p:TargetFramework=netcoreapp3.1
+%~dp0Benchmarks\bin\x64\Release\netcoreapp3.1\Benchmarks.exe -filter *
+rem Clean project to prevent mismatch scenarios with the typical benchmark.cmd scenario.
+msbuild Benchmarks\Benchmarks.csproj -t:restore -t:clean /p:BenchmarkWinmdSupport=true /p:platform=x64 /p:configuration=release /p:TargetFramework=netcoreapp3.1 >nul
\ No newline at end of file
diff --git a/cswinrt.sln b/cswinrt.sln
index e9b85aa6f..40d1ee8d6 100644
--- a/cswinrt.sln
+++ b/cswinrt.sln
@@ -54,6 +54,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nuget", "Nuget", "{5A94EFDF
nuget\SignConfig.xml = nuget\SignConfig.xml
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{B34C96F4-3660-4B2D-8ABD-A4B428166DC7}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BenchmarkComponent", "TestWinRT\BenchmarkComponent\BenchmarkComponent.vcxproj", "{78D85F23-7CB1-44A1-9238-6DF2C76754E4}"
+ ProjectSection(ProjectDependencies) = postProject
+ {6ACFD2B2-E8AA-4CD4-AAD8-213CE8BB2637} = {6ACFD2B2-E8AA-4CD4-AAD8-213CE8BB2637}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmark", "Projections\Benchmark\Benchmark.csproj", "{03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -208,6 +217,48 @@ Global
{0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x64.Build.0 = Release|x64
{0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x86.ActiveCfg = Release|x86
{0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x86.Build.0 = Release|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|ARM.ActiveCfg = Debug|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|ARM64.ActiveCfg = Debug|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|x64.ActiveCfg = Debug|x64
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|x64.Build.0 = Debug|x64
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|x86.ActiveCfg = Debug|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|x86.Build.0 = Debug|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|Any CPU.ActiveCfg = Release|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|ARM.ActiveCfg = Release|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|ARM64.ActiveCfg = Release|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|x64.ActiveCfg = Release|x64
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|x64.Build.0 = Release|x64
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|x86.ActiveCfg = Release|x86
+ {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Release|x86.Build.0 = Release|x86
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|ARM.ActiveCfg = Debug|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|ARM64.ActiveCfg = Debug|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|x64.ActiveCfg = Debug|x64
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|x64.Build.0 = Debug|x64
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|x86.ActiveCfg = Debug|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Debug|x86.Build.0 = Debug|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|Any CPU.ActiveCfg = Release|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|ARM.ActiveCfg = Release|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|ARM64.ActiveCfg = Release|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|x64.ActiveCfg = Release|x64
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|x64.Build.0 = Release|x64
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|x86.ActiveCfg = Release|Win32
+ {78D85F23-7CB1-44A1-9238-6DF2C76754E4}.Release|x86.Build.0 = Release|Win32
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|ARM.ActiveCfg = Debug|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|ARM64.ActiveCfg = Debug|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|x64.ActiveCfg = Debug|x64
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|x64.Build.0 = Debug|x64
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|x86.ActiveCfg = Debug|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Debug|x86.Build.0 = Debug|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|Any CPU.ActiveCfg = Release|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|ARM.ActiveCfg = Release|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|ARM64.ActiveCfg = Release|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x64.ActiveCfg = Release|x64
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x64.Build.0 = Release|x64
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x86.ActiveCfg = Release|x86
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -216,6 +267,7 @@ Global
{C6D580C5-7037-4733-B933-916FF400AFE2} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B}
{FFA9A78B-F53F-43EE-AF87-24A80F4C330A} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B}
{0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B}
+ {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5AE8C9D7-2613-4E1A-A4F2-579BAC28D0A2}
diff --git a/get_testwinrt.cmd b/get_testwinrt.cmd
index 971973dc6..d78f9490d 100644
--- a/get_testwinrt.cmd
+++ b/get_testwinrt.cmd
@@ -12,7 +12,7 @@ git checkout -f master
if ErrorLevel 1 popd & exit /b !ErrorLevel!
git fetch -f
if ErrorLevel 1 popd & exit /b !ErrorLevel!
-git reset -q --hard 427f66c2cd0837e81005a840472b0ef1a1d8639d
+git reset -q --hard 6d72afbcb51ab3981c6cd620d24954020f4d2bbc
if ErrorLevel 1 popd & exit /b !ErrorLevel!
echo Restoring Nuget
..\.nuget\nuget.exe restore