Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b0e8823
Centralize build props and AOT/NativeAOT settings
Sergio0694 Jun 23, 2026
19de71b
Remove redundant project properties now centralized in Directory.Buil…
Sergio0694 Jun 23, 2026
41d21b8
Remove redundant AOT/NativeAOT properties now centralized in Director…
Sergio0694 Jun 23, 2026
711d913
Use centralized $(DotNetVersion) for project target frameworks
Sergio0694 Jun 23, 2026
b594f63
Remove project suppressions that are no longer necessary
Sergio0694 Jun 23, 2026
2cb904f
Fix build breaks introduced by the centralized build properties
Sergio0694 Jun 23, 2026
f9e97c5
Centralize AllowUnsafeBlocks in Directory.Build.props
Sergio0694 Jun 23, 2026
c915f9e
Fix WinRT.Host.Shim build warnings under the centralized analysis rules
Sergio0694 Jun 23, 2026
44888a5
Fix WinRT.Generator.Tasks build warnings under the centralized analys…
Sergio0694 Jun 23, 2026
ee19beb
Relax strict analysis for the local-only Perf/ResultsComparer tool
Sergio0694 Jun 23, 2026
498ea70
Fix CS8632 nullable annotation warnings in DiagnosticTests
Sergio0694 Jun 23, 2026
eec862e
Suppress CS8305 for the generated Windows projections
Sergio0694 Jun 23, 2026
614909f
Clean up projections Directory.Build.props
Sergio0694 Jun 23, 2026
07dce7e
Add CS1591 to WarningsNotAsErrors
Sergio0694 Jun 23, 2026
f3f2a20
Only generate documentation files for product projects
Sergio0694 Jun 24, 2026
2b7ef79
Only enforce code-style rules for product projects
Sergio0694 Jun 24, 2026
0c6bf00
Centralize the product-project check into an IsPublicProject property
Sergio0694 Jun 24, 2026
69336bd
Suppress CSWINRT3001 in UnitTest for intentional private-API testing
Sergio0694 Jun 24, 2026
d648afd
Suppress CS8981 from CsWin32-generated code in UnitTest and OOPExe
Sergio0694 Jun 24, 2026
01fd1a2
Suppress CA1416 in BuildDeterminismComponent
Sergio0694 Jun 24, 2026
593c0e8
Fix remaining warnings-as-errors in UnitTest and Benchmarks
Sergio0694 Jun 24, 2026
14ba87c
Suppress CA1416 in UnitTest for intentional platform-warning tests
Sergio0694 Jun 24, 2026
5df4f06
Suppress IL2050 and CA1420 from CsWin32-generated P/Invoke in UnitTest
Sergio0694 Jun 24, 2026
9043c1b
Suppress MSTEST0003 for the private manual PnP helper in UnitTest
Sergio0694 Jun 24, 2026
caaa168
Fix remaining warnings-as-errors in OOPExe and UnitTest
Sergio0694 Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions src/Authoring/WinRT.Host.Shim/Module.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// TODO: consider embedding this as a resource into WinRT.Host.dll,
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// TODO: consider embedding this as a resource into WinRT.Host.dll,
// to simplify deployment

using System;
Expand All @@ -15,45 +18,54 @@

namespace WinRT.Host;

/// <summary>
/// Provides the activation factory entry point used by the native <c>WinRT.Host</c> shim to host managed Windows Runtime components.
/// </summary>
public static class Shim
{
private const int S_OK = 0;
private const int E_NOINTERFACE = unchecked((int)0x80004002);
private const int REGDB_E_READREGDB = unchecked((int)0x80040150);
private const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)(0x80040111));
private const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)0x80040111);

/// <summary>
/// Delegate matching the native signature used to retrieve an activation factory from a hosted component.
/// </summary>
public unsafe delegate int GetActivationFactoryDelegate(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory);

private unsafe delegate void* ManagedExportsGetActivationFactoryDelegate(ReadOnlySpan<char> activatableClassId);

private static HashSet<string> _InitializedResolvers;

/// <summary>
/// Retrieves the activation factory for a runtime class from the specified target assembly, loading it into the default load context.
/// </summary>
public static unsafe int GetActivationFactory(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory)
{
*activationFactory = IntPtr.Zero;

var targetAssembly = HStringMarshaller.ConvertToManaged((void*)hstrTargetAssembly);
var runtimeClassId = HStringMarshaller.ConvertToManaged((void*)hstrRuntimeClassId);
string targetAssembly = HStringMarshaller.ConvertToManaged((void*)hstrTargetAssembly);
string runtimeClassId = HStringMarshaller.ConvertToManaged((void*)hstrRuntimeClassId);

try
{
Assembly assembly = LoadInDefaultContext(targetAssembly);

// ABI.<ModuleName>.ManagedExports.GetActivationFactory(ReadOnlySpan<char>) -> void*
string moduleName = Path.GetFileNameWithoutExtension(targetAssembly);
var managedExportsType = assembly.GetType($"ABI.{moduleName}.ManagedExports");
Type managedExportsType = assembly.GetType($"ABI.{moduleName}.ManagedExports");
if (managedExportsType == null)
{
return REGDB_E_READREGDB;
}
var GetActivationFactory = managedExportsType.GetMethod("GetActivationFactory", new Type[] { typeof(ReadOnlySpan<char>) });
MethodInfo GetActivationFactory = managedExportsType.GetMethod("GetActivationFactory", [typeof(ReadOnlySpan<char>)]);
if (GetActivationFactory == null)
{
return REGDB_E_READREGDB;
}
// ReadOnlySpan<char> is a ref struct and can't be used with MethodInfo.Invoke.
// Use a delegate to call the method directly.
var del = GetActivationFactory.CreateDelegate<ManagedExportsGetActivationFactoryDelegate>();
ManagedExportsGetActivationFactoryDelegate del = GetActivationFactory.CreateDelegate<ManagedExportsGetActivationFactoryDelegate>();
void* factory = del(runtimeClassId.AsSpan());
if (factory == null)
{
Expand All @@ -72,25 +84,21 @@ private static Assembly LoadInDefaultContext(string targetAssembly)
{
if (_InitializedResolvers == null)
{
Interlocked.CompareExchange(ref _InitializedResolvers, new HashSet<string>(StringComparer.OrdinalIgnoreCase), null);
_ = Interlocked.CompareExchange(ref _InitializedResolvers, new HashSet<string>(StringComparer.OrdinalIgnoreCase), null);
}

lock (_InitializedResolvers)
{
if (!_InitializedResolvers.Contains(targetAssembly))
{
var resolver = new AssemblyDependencyResolver(targetAssembly);
AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) =>
AssemblyDependencyResolver resolver = new(targetAssembly);
AssemblyLoadContext.Default.Resolving += (assemblyLoadContext, assemblyName) =>
{
string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
}
return null;
return assemblyPath != null ? assemblyLoadContext.LoadFromAssemblyPath(assemblyPath) : null;
};

_InitializedResolvers.Add(targetAssembly);
_ = _InitializedResolvers.Add(targetAssembly);
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/Authoring/WinRT.Host.Shim/WinRT.Host.Shim.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!--
This project is meant to be used for hosting scenarios with CoreCLR, so trimming and AOT
aren't supported by design. Opt out of the centralized 'IsAotCompatible' default so the
trim/AOT analyzers don't run on it.
-->
<IsAotCompatible>false</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>14.0</LangVersion>
<TargetFramework>$(DotNetVersion)</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<!--
Suppress the following warning:
Expand All @@ -28,28 +26,6 @@
-->
<DebugType>embedded</DebugType>

<!-- Treat all warnings as errors, only in release configurations (same as in the runtime) -->
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</CodeAnalysisTreatWarningsAsErrors>

<!-- Enable the latest warning wave (same as in the runtime) -->
<AnalysisLevel>latest</AnalysisLevel>

<!-- Import the global configs from the CodeStyle package (enables all IDExxxx warnings)-->
<AnalysisLevelStyle>latest-all</AnalysisLevelStyle>

<!-- Enforce all code style rules during build (this replaces referencing Microsoft.CodeAnalysis.CSharp.CodeStyle) -->
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>

<!-- Suppress ref safety warnings in unsafe contexts (same as in the runtime) -->
<NoWarn>$(NoWarn);CS8500</NoWarn>

<!-- Enable the compiler strict mode (same as in the runtime) -->
<Features>strict</Features>

<!-- Generate documentation files (same workaround as in the runtime) -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>

<!-- Roslyn settings -->
<IsRoslynComponent>true</IsRoslynComponent>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
Expand Down
4 changes: 2 additions & 2 deletions src/Benchmarks/EventPerf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public object IntEventOverhead()
ClassWithMarshalingRoutines instance = new ClassWithMarshalingRoutines();
int z;
System.EventHandler<int> s = (object sender, int value) => z = value;
return instance;
GC.KeepAlive(s);
return instance;
}

[Benchmark]
Expand Down Expand Up @@ -134,8 +134,8 @@ public object AddAndRemoveIntEventOnNewEventSource()
System.EventHandler<int> s = (object sender, int value) => z = value;
instance.IntPropertyChanged += s;
instance.IntPropertyChanged -= s;
return instance;
GC.KeepAlive(s);
return instance;
}

[Benchmark]
Expand Down
88 changes: 86 additions & 2 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
<MicrosoftWinAppSDKIxpVersion>1.8.251104001</MicrosoftWinAppSDKIxpVersion>
<MicrosoftWinAppSDKFoundationVersion>1.8.251104000</MicrosoftWinAppSDKFoundationVersion>
<MicrosoftWebWebView2Version>1.0.3179.45</MicrosoftWebWebView2Version>

<!-- Debug symbol properties -->
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
<LangVersion>preview</LangVersion>
<NoWarn>8305;0618</NoWarn>

<GenerateTestProjection Condition="'$(GenerateTestProjection)$(Configuration)' == 'Release'">true</GenerateTestProjection>
<GenerateTestProjection Condition="'$(GenerateTestProjection)' == ''">false</GenerateTestProjection>
<CsWinRTGenerateProjection>$(GenerateTestProjection)</CsWinRTGenerateProjection>
Expand All @@ -37,6 +38,89 @@
<CsWinRTMessageImportance>high</CsWinRTMessageImportance>
</PropertyGroup>

<PropertyGroup Label="Globals" Condition="'$(MSBuildProjectExtension)' == '.csproj'">

<!--
Classify "public" (product) projects, ie. the ones we actually ship, which are all named with a 'WinRT.'
prefix. This is used below to scope the repo's strict analysis (code-style enforcement and XML documentation)
to just those projects, leaving non-product projects (tests, samples, benchmarks, the local projection
projects, etc.) out of it. Note: 'StartsWith(...)' returns 'true'/'false', so compare against 'true' below.
-->
<IsPublicProject>$(MSBuildProjectName.StartsWith('WinRT.'))</IsPublicProject>

<!--
Centralize the .NET version used by all projects. This makes it much easier to try out different
.NET versions without touching every project file, as well as reverting any changes, if needed.
-->
<DotNetVersion>net10.0</DotNetVersion>

<!-- Enforce the same language version for all projects -->
<LangVersion>14.0</LangVersion>

<!-- Allow unsafe code in all projects (the runtime and build tools rely heavily on pointers and other unsafe constructs for interop) -->
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<!--
Treat all warnings as errors, only in release configurations. This allows the dev inner loop to remain
convenient, without errors while testing things out and prototyping things, while still ensuring that
all CI runs and official builds (including from PRs) will fail if any warnings are not addressed.
-->
<TreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors Condition="'$(Configuration)' == 'Release'">true</CodeAnalysisTreatWarningsAsErrors>

<!--
Enable the latest warning wave, which shows additional warnings for invalid language features that are disabled by default.
For additional info, see https://docs.microsoft.com/dotnet/csharp/language-reference/compiler-messages/warning-waves.
-->
<AnalysisLevel>latest</AnalysisLevel>

<!--
Enable all IDExxxx code-style rules and enforce them during build, but only for the public (product) projects.
We don't want to impose the repo's strict code-style rules on non-product projects (tests, samples, benchmarks,
the local projection projects, etc.): they would surface a large number of non-actionable IDExxxx warnings (and
'EnableGenerateDocumentationFile' from IDE0005, which requires docs to be generated) on code we don't ship,
including generated projection code.
-->
<AnalysisLevelStyle Condition="'$(IsPublicProject)' == 'true'">latest-all</AnalysisLevelStyle>
<EnforceCodeStyleInBuild Condition="'$(IsPublicProject)' == 'true'">true</EnforceCodeStyleInBuild>

<!--
Suppress ref safety warnings in unsafe contexts (see https://github.com/dotnet/csharplang/issues/6476).
This is used eg. to replace Unsafe.SizeOf<T>() calls with just sizeof(T), or to just use raw pointers to
reinterpret references to managed objects when it is safe to do so. The warnings are not necessary in this
context, since in order to use these APIs the caller already has to be in an unsafe context.
-->
<NoWarn>$(NoWarn);CS8500</NoWarn>

<!--
Temporarily suppress warnings about using CsWinRT 3.0 while in preview:
NETSDK1229: "Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is
currently in preview. The current project is targeting the Windows SDK version ''. If this is not intended, change
the revision number to '0' to use CsWinRT 2.x instead.".
-->
<NoWarn>$(NoWarn);NETSDK1229</NoWarn>

<!--
Enable the compiler strict mode (see https://www.meziantou.net/csharp-compiler-strict-mode.htm).
This (poorly documented) mode enables additional warnings for incorrect usages of some features.
For instance, this will warn when using the == operator to compare a struct with a null literal.
-->
<Features>$(Features);strict</Features>

<!--
Generate documentation files, but only for the public (product) projects. We don't care about XML docs for
non-product projects (tests, samples, benchmarks, the local projection projects, etc.), and enabling it there
would also enforce 'CS1591' on all their public members and on generated projection code, which isn't actionable.

In theory this should only be enabled for published, non source generator projects. However, it's enabled for
all product projects (including the source generator) to work around https://github.com/dotnet/roslyn/issues/41640.
Until that's fixed, source generators will also produce an .xml file with their documentation. Note that this
doesn't really impact NuGet packages, since the analyzer binaries are packed manually after build, so the .xml
files aren't included. Once that issue is fixed, this should be restricted to published projects only.
-->
<GenerateDocumentationFile Condition="'$(IsPublicProject)' == 'true'">true</GenerateDocumentationFile>
</PropertyGroup>

<!-- Set overrides to point to folder where they are built in solution -->
<PropertyGroup>
<CsWinRTGenTasksAssembly>$(MSBuildThisFileDirectory)WinRT.Generator.Tasks\bin\$(Configuration)\netstandard2.0\WinRT.Generator.Tasks.dll</CsWinRTGenTasksAssembly>
Expand Down
Loading