Improve hosting support experience#2417
Open
manodasanW wants to merge 104 commits into
Open
Conversation
Member
manodasanW
commented
May 11, 2026
- Added some native targets for where if CsWinRT package is included by a native app, it will handle making sure the right WinRT.Host.dll gets copied which was an issue in the past when the component was AnyCPU but app was like x64. It will also handle making the JIT hosting scenarios less complicated in our new model given we need a single-entry point for TypeMap to work.
- To improve the JIT hosting scenarios, WinRT.Host.dll now calls into WinRT.Component.dll which serves as the main entry point (and sets it) and then redirects it to each components activation factory to try to find the type.
- Added sample for AOT scenarios too where we are not doing something similar to JIT but instead providing the developer a way to create a project to merge all the components that they want to use and merge the activation factories for them. A developer can also choose to publish each component individually which is part of why we are not automating this process. This is similar to the experience before.
- Added missing binaries in nuget and also cleaned up the included targets.
- There are scenarios where WinRT.Projection.dll may not get generated, so adding handling for that.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The shim is a hosting asset (only deployed for authored components), not a managed reference for consumers. Placing it under lib\net10.0 caused NuGet/SDK to auto-resolve it as a compile reference everywhere, requiring CsWinRTRemoveHostingDllReferences to scrub it from multiple item groups. Moving it under hosting\ matches WinRT.Host.dll's layout, removes the auto-resolution problem entirely, and lets the targets logic in Authoring.targets be the single source of deployment for the shim. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WinRT.Host.dll lives under hosting\<arch>\native\ which NuGet does not auto-resolve, so the runtimes\**\native\WinRT.Host.dll Remove entries never matched anything. Leftover from a previous layout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Microsoft.Windows.CsWinRT.Authoring.targets and Microsoft.Windows.CsWinRT.Authoring.Transitive.targets were commented out in the nuspec, and Microsoft.Windows.CsWinRT.Authoring.WinMD.targets was missing entirely. All three are needed by component-authoring builds (CsWinRTComponent=true). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Neither targets file is imported by anything in CsWinRT 3.0 nor packaged in the NuGet. They are CsWinRT 2.x leftovers. Updated cswinrt.slnx to drop the entries and add the Authoring.WinMD.targets entry alongside the other authoring targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pack Microsoft.Windows.CsWinRT.Native.targets at build\native\Microsoft.Windows.CsWinRT.targets so NuGet auto-imports it for native (vcxproj) consumers via the package's <id>.targets convention. Drop the inline CsWinRTBuildComponentInterop target from the AuthoringConsumptionTest vcxproj in favor of importing Native.targets directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The synthesized aggregator csproj ProjectReferences each CsWinRT component, which flows the component's .winmd into the aggregator's resolved reference set. The .NET SDK rejects that with NETSDK1130 (.NET 5+ disallows direct .winmd references). The aggregator doesn't import CsWinRT.targets (no PackageReference), so the standard CsWinRTRemoveWinMDReferences target isn't available. Inline a winmd-scrub target into the generated aggregator csproj content. It runs between ResolveProjectReferences and ResolveAssemblyReferences, removing any .winmd from _ResolvedProjectReferencePaths (and defensively from Reference/ReferencePath) so the SDK's NETSDK1130 check sees no winmd references. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The synthesized aggregator csproj had no PackageReference to cswinrt and no other path to import cswinrt's targets, so its CsWinRTBuildForNativeConsumer / CsWinRTGenerateInteropAssembly properties had no consumer in scope. cswinrtinteropgen never ran on the aggregator, and the merged WinRT.Component.dll / WinRT.Interop.dll never got produced. The .winmd scrub from the prior commit handled NETSDK1130 but didn't fix the missing consumer of those properties. Resolution: have Native.targets compute the absolute paths of Microsoft.Windows.CsWinRT.props and Microsoft.Windows.CsWinRT.targets relative to its own location and inject explicit <Import> elements at the top and bottom of the aggregator csproj content. This brings cswinrt's full target tree into the aggregator's evaluation, so CsWinRTGen.targets correctly wires cswinrtinteropgen and CsWinRTRemoveWinMDReferences scrubs the .winmd inputs (replacing the inline scrub from the prior commit). Zero version skew (the imported targets are the same cswinrt that's running Native.targets) and no NuGet restore overhead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When Native.targets is consumed from the in-repo source tree (e.g. by the AuthoringConsumptionTest vcxproj's local-dev <Import>), it sits at nuget\Microsoft.Windows.CsWinRT.Native.targets next to the props/targets. When consumed from the shipped package, it sits at build\native\Microsoft.Windows.CsWinRT.targets and the props/targets are one folder up at build\. The previous fixed '..\' path worked only for the package layout and failed in the source layout with MSB4019. Probe both relative locations via Exists() and pick whichever resolves first, preferring the package layout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move nuget\Microsoft.Windows.CsWinRT.Native.targets to nuget\native\Microsoft.Windows.CsWinRT.targets so the source tree matches the shipped package layout (build\native\Microsoft.Windows.CsWinRT.targets). Native.targets's '..\Microsoft.Windows.CsWinRT.props' / '..\Microsoft.Windows.CsWinRT.targets' relative imports now resolve identically in both source and package layouts; no Exists() probing or repo-specific special casing required. Update the nuspec source path and the AuthoringConsumptionTest vcxproj's local-dev import to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CsWinRTGen.targets has many import-time PropertyGroups that derive paths from $(IntermediateOutputPath) (e.g. CsWinRTGeneratorInteropAssemblyDirectory, _RunCsWinRTGeneratorPropertyInputsCachePath, _CsWinRTGeneratorMergedProjectionAssemblyPath, _CsWinRTGeneratorComponentAssemblyPath, _CsWinRTSdkProjectionAssemblyPath, _CsWinRTRefAssemblyPath, CsWinRTGeneratorForwarderAssemblyDirectory). When the aggregator imports cswinrt.targets via the implicit <Project Sdk=...> form, the import happens before Sdk.targets sets IntermediateOutputPath, so all those derived paths evaluate to empty leading to MSB4044 (RunCsWinRTMergedProjectionGenerator missing GeneratedAssemblyDirectory) and similar. Switch the aggregator to the explicit <Project> form with explicit <Import Sdk.props/Sdk.targets> tags so we can place the cswinrt.targets <Import> after Sdk.targets. cswinrt.props still goes between Sdk.props and Sdk.targets so its BeforeMicrosoftNETSdkTargets contribution is in scope when Sdk.targets evaluates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The repo's Directory.Build.targets cascades into the synthesized aggregator csproj that Native.targets generates under src\_build\...\obj\cswinrt\, but the aggregator's restore doesn't resolve Microsoft.DiaSymReader.Pdb2Pdb (the package is added implicitly via Directory.Build.targets, but GeneratePathProperty doesn't always materialize for synthesized projects). Result: PkgMicrosoft_DiaSymReader_Pdb2Pdb is empty, the Pdb2Pdb path collapses to '\tools\Pdb2Pdb.exe', and the Exec fails. Gate the target on the package property being non-empty. This is a defensive change that benefits any csproj where Pdb2Pdb isn't resolvable, not aggregator-specific. Keeps the shipping artifact (Native.targets) free of repo-local concerns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .NET SDK's default IntermediateOutputPath injects \ for non-AnyCPU builds, so an x86/x64/ARM64 aggregator outputs to obj\<Platform>\<Config>\<TFM>\. Native.targets's post-build Copy reads from a fixed obj\<Config>\<TFM>\ path (no platform). Path mismatch -> Exists() guards no-op -> WinRT.Component.dll / WinRT.Interop.dll / projection assemblies never bin-place -> all 49 AuthoringConsumptionTest activation tests fail with 'Unknown C++ exception' at first activation. Pass IntermediateOutputPath and BaseIntermediateOutputPath as global properties to the aggregator's Restore/Build invocations, pinning them to the same path Native.targets will read from. Both sides agree by construction; no SDK platform-folder logic to mirror. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Embed the pinned BaseIntermediateOutputPath / IntermediateOutputPath inside the aggregator csproj's PropertyGroup rather than passing them on the parent MSBuild call. Project-local properties stay scoped to the aggregator; global properties propagate transitively into ProjectReferences (component csproj, then the C++ cswinrt.vcxproj it references), where the legacy packages.config NuGet resolver fails because the inherited intermediate path has no lockfile for that vcxproj's packages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pin the aggregator's IntermediateOutputPath only up through Configuration. The .NET SDK appends a TargetFramework segment to IntermediateOutputPath in its post-evaluation pass; previously we passed the full path including TFM and the SDK appended a second TFM, producing obj\Debug\net10.0\net10.0\. cswinrtinteropgen wrote merged DLLs there while Native.targets's post-build Copy step read from the single-TFM path. Now the pinned PropertyGroup value ends at <Config>\\, the SDK appends <TFM>\\, and the final on-disk path matches _CsWinRTTempProjectIntermediateDir that the Copy step reads from. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The temp-aggregator approach can't see component assemblies in its ReferencePath because Authoring.targets reports the component's primary output as the .winmd (with the .dll only as ManagedImplementation metadata). After CsWinRTRemoveWinMDReferences scrubs the winmd, nothing from the component is left in ReferencePath, so the aggregator's component projection generator finds no component types and emits no WinRT.Component.dll. Replace the aggregator with per-component direct MSBuild dispatch (matching what the original inline target in AuthoringConsumptionTest.vcxproj was doing): for each detected CsWinRTComponent project reference, invoke its Build with CsWinRTBuildForNativeConsumer=true so the component's own pipeline produces the merged hosting bundle in its own intermediate dir, then copy from there. Translates the C++ \='Win32' to the managed platform 'x86' for the path math. Single-component scenarios (the only ones currently exercised by tests) now work end to end. Multi-component aggregation - which has type-map duplicate-key concerns and was the original motivation for the aggregator csproj - is deferred as separate work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring back the temp-aggregator csproj that ProjectReferences every detected CsWinRTComponent and runs cswinrt's full generator pipeline once across the union, so a single deduplicated WinRT.Interop.dll / WinRT.Component.dll / projection bundle is produced and the multi-component scenario (preventing type-map duplicate-key collisions across components) keeps working. Fix the previous structural blocker: each component's TargetPathWithTargetPlatformMoniker reports the .winmd as primary, with the .dll only as ManagedImplementation metadata, so after CsWinRTRemoveWinMDReferences nothing from the component remained in the aggregator's ReferencePath. Native.targets now captures %(_ResolvedProjectReferencePaths.ManagedImplementation) for component refs and emits an explicit <Reference Include='...componentdll' Private='false'> per component into the aggregator csproj, alongside the existing <ProjectReference>. cswinrt's component projection generator now sees the [WindowsRuntimeComponentAssembly] markers and produces WinRT.Component.dll. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch AuthoringTest's TargetFramework from net10.0 to net10.0-windows10.0.26100.1 (the CsWinRT 3.0 convention - .1 revision identifies the 3.0 stack). Update AuthoringConsumptionTest.vcxproj's hardcoded references to the AuthoringTest output path to match. Now that the aggregator's TargetFramework propagates from the component as a Windows-targeted TFM, Microsoft.NET.Windows.targets imports into the aggregator and its built-in RemoveManagedWinRTComponentWinMDReferences / AddWinRTComponentImplementationReference targets fire automatically. They scrub the component's .winmd from _ResolvedProjectReferencePaths and re-inject the managed implementation .dll (from ManagedImplementation metadata that Authoring.targets:166 already attaches) as an explicit Reference. So cswinrt's component projection generator sees [WindowsRuntimeComponentAssembly] on the .dll naturally. Drop the manual <Reference Include=...> / _CsWinRTComponentManagedImplementationPaths plumbing in Native.targets - the SDK now provides this for us. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .NET 10 SDK auto-injects Microsoft.Windows.SDK.NET.Ref.CsWinRT3.Windows (and .CsWinRT3.Xaml when UseUwp=true) for projects targeting a CsWinRT3 TFM (revision .1). The repo previously only scrubbed the 2.x-era names; AuthoringTest's recent move to net10.0-windows10.0.26100.1 means it now picks up the CsWinRT3 variants. Add removes for those so repo-local builds keep using cswinrt's locally-generated projection rather than the SDK's pinned reference assemblies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 3bf45d2.
This reverts commit c3675b2.
AuthoringTest builds against the locally-generated WinAppSDK projection (via WinAppSDK.csproj), not the strong-named Microsoft.WinUI shipped in the WindowsAppSDK package. With AuthoringTest's TFM at net10.0 (non-Windows), the package's assets didn't activate. With net10.0-windows10.0.26100.1, they do, and the package's Microsoft.WinUI 3.0.0.0 (PKT de31ebe4ad15742b) conflicts with the locally-built unsigned 1.0.0.0, surfacing as MSB3243 + NETSDK1148 ('a referenced assembly was compiled using a newer version of Microsoft.Windows.SDK.NET.dll').
Mirror the existing ExcludeAssets=all pattern already used for Microsoft.Web.WebView2 on the next line.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On Windows-targeted TFMs the .NET SDK's RemoveManagedWinRTComponentWinMDReferences runs AfterTargets=ResolveProjectReferences and scrubs CsWinRT-component winmds from _ResolvedProjectReferencePaths. Our CsWinRTRemoveWinMDReferences runs later (AfterTargets=ResolveReferences) and only sees the post-scrub state, so component winmds never reach CsWinRTInputs / _WinMDPathsList. cswinrtprojectiongen then runs without the winmd in --winmd-paths, finds no component types, emits no WinRT.Component.dll, and cswinrtinteropgen fails with CSWINRTINTEROPGEN0091. Add CsWinRTCaptureProjectReferenceWinMDs that runs AfterTargets=ResolveProjectReferences and BeforeTargets=RemoveManagedWinRTComponentWinMDReferences. It captures Extension=.winmd + Implementation=WinRT.Host.dll items from _ResolvedProjectReferencePaths into CsWinRTInputs before the SDK removes them. _WinMDPathsList now includes the component winmds and cswinrt's generators see the component types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit b3d1846.
This reverts commit 7e2e354.
… build""" This reverts commit 8eae4b2.
ExcludeAssets=all also excludes the build asset, which removes WinAppSDK's MakePri targets from the import graph. That's why AuthoringTest.pri was no longer being generated under the new Windows-targeted TFM. Narrow the exclusion so the Microsoft.WinUI 3.0.0.0-vs-1.0.0.0 compile/runtime conflict is still suppressed but the PRI generation step still runs.
The .1-revision Windows TFM causes the .NET SDK to inject Microsoft.Windows.SDK.NET.Ref.CsWinRT3.{Windows,Xaml}, which carry the prebuilt SDK projection assemblies. This repo builds those projections from source, so let the local copies be the only ones in scope by removing the SDK-injected framework references.
The aggregator csproj sets CsWinRTBuildForNativeConsumer=true on itself, but that property doesn't flow into transitive ProjectReferences by default. Each referenced component (e.g. AuthoringTest.csproj) compiles with the property unset, so TypeMapAssemblyTargetGenerator's gate (isOutputTypeExe || isPublishAotLibrary || isBuildForNativeConsumer) stays off. Result: no [TypeMapAssemblyTarget] assembly attributes are emitted on the component .dll, and runtime activation through WinRT.Host fails because the type map groups can't be resolved. Set the property explicitly via AdditionalProperties on the synthesized ProjectReference so each component sees it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AuthoringTest's Directory.Build.props sets PublishAot=true; SelfContained=true for Release|x64 (so the single-component AuthoringConsumptionTest can publish it as a self-contained native dll). When AuthoringConsumptionTest2.AOT.csproj PRs AuthoringTest, that block fires too, and AuthoringTest copies the WindowsAppSDK runtime payload to its bin. .AOT then publishes itself self-contained, pulling those runtimes from both AuthoringTest's bin and its own NuGet restore - NETSDK1152 duplicate publish output errors. Pass PublishAot=false; SelfContained=false; NativeLib=; OutputType=Library; CustomNativeMain=false as global properties on the PR. Globals override the Directory.Build.props PropertyGroup, so AuthoringTest builds as a plain managed library here. The merged AOT host handles all AOT linking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WinAppSDK has its own self-contained mode property separate from .NET's SelfContained. Setting OutputType=Library already suppresses the WinAppSDK default, but pass WindowsAppSDKSelfContained=false explicitly as defense in depth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 49f4422.
OutputType=Library passed as a global propagated transitively through AuthoringTest's PRs (Projections/Windows -> WinRT.Impl.Generator etc.), overriding those projects' OutputType=Exe and breaking compilation with CS8805 (top-level statements require executable). The duplicate publish output payload was the WinAppSDK runtime files, copied into AuthoringTest's bin because WinAppSDK auto-defaults WindowsAppSDKSelfContained=true when OutputType != Library. Set WindowsAppSDKSelfContained=false directly instead - same effect, no collateral damage to descendant project builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AuthoringTest's Directory.Build.props sets PublishAot=true; OutputType=Exe; SelfContained=true; etc for Release|x64 - that's what makes the standalone single-component AOT publish in AuthoringConsumptionTest work. When AuthoringConsumptionTest2.AOT.csproj PRs AuthoringTest as a managed input for the merged AOT host, that block fights us. Trying to override the props as PR globals doesn't work cleanly: - OutputType=Library global propagates to AuthoringTest's transitive PRs (Projections/Windows -> WinRT.Impl.Generator etc.), overriding their OutputType=Exe and breaking compile with CS8805. - Without OutputType=Library, WinAppSDK either pulls its runtime payload into AuthoringTest's bin (NETSDK1152 dupes) or errors out demanding the Microsoft.WindowsAppSDK.Runtime PackageReference. Add an opt-out property _AuthoringTestSkipAotPublishConfig to the gating Condition. The merged AOT csproj sets that single property as a global on its PR, suppressing the AOT block entirely. AuthoringTest stays a plain CsWinRT component library - exactly what the merged host wants. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .AOT.csproj has <PublishAot>true</PublishAot> in its PropertyGroup, but NuGet's Restore phase invoked via <MSBuild Targets='Restore;Publish'> didn't see it as a global property and didn't pull runtime.win-x64. microsoft.dotnet.ilcompiler into the restore graph. Publish then ran as a plain self-contained JIT publish, copying the whole .NET 10 framework plus clrjit.dll to bin\publish, producing a 16KB managed .AOT.dll instead of an AOT-compiled native dll. Tests crashed at activation because the manifest pointed at a managed library RoActivateInstance couldn't activate. Pass PublishAot=true explicitly as a global property on the <MSBuild> call so both Restore and Publish phases see it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Passing PublishAot=true as a global from CsWinRTLink (so NuGet Restore pulls the ILC package) propagates transitively to managed-only PRs (Projections/Windows -> WinRT.Generator.Tasks targeting netstandard2.0) and triggers NETSDK1207 'Ahead-of-time compilation is not supported for the target framework'. Add <UndefineProperties>PublishAot</UndefineProperties> on each PR so the global is stripped at the boundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…el project
Previous attempt invoked the .AOT csproj from CsWinRTLink via <MSBuild
Targets='Restore;Publish'> with custom globals (PublishAot=true,
RuntimeIdentifier=win-x64). Those globals propagated transitively through
the .AOT csproj's PR closure (Projections/Windows -> WinRT.Generator.Tasks),
caused MSBuild to see different project instance hashes than the slnx-level
invocations of the same projects, and ran a second concurrent build that
raced for shared bin\Release\<TFM>\<assembly>.dll outputs (file lock
errors). Patching with <UndefineProperties> per global was unmaintainable
- every divergent global at every PR boundary needed enumeration.
Move .AOT.csproj back into slnx as a top-level project, BuildDependency
on AuthoringTest + AuthoringTest2, gated to build only for x64 Release.
This way:
- .AOT.csproj's evaluation uses the same slnx-level globals as everything
else; descendants reuse the slnx-level project instances; no race.
- Original contamination (commit ad69028) is avoided because .AOT.csproj's
PR to AuthoringTest passes _AuthoringTestSkipAotPublishConfig=true,
which suppresses AuthoringTest's Directory.Build.props PublishAot block.
AuthoringTest evaluates as a plain library here (different instance from
AuthoringConsumptionTest's PublishAot-fired Publish-time instance, but
different bin output paths so no race).
CsWinRTLink target in AuthoringConsumptionTest2.vcxproj now just picks up
the .AOT.dll from its publish folder and adds it to ReferenceCopyLocalPaths.
No <MSBuild> sub-invocation, no Restore/Publish from inside the vcxproj.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Without <Platform Solution='*|x64' Project='x64'/>, slnx defaulted .AOT.csproj to AnyCPU for Solution=Release|x64. AnyCPU propagated through its PR to AuthoringTest, which then tried to locate cswinrt.exe at _build\AnyCPU\Release\ (does not exist - cswinrt is built for x64). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When AuthoringConsumptionTest2.AOT.csproj's ProjectReference to AuthoringTest sets <Properties>_AuthoringTestSkipAotPublishConfig=true</Properties>, that global property propagates through AuthoringTest's PR closure - including its PR to Projections/Windows.csproj. That gives Projections/Windows a different global-property hash than its slnx-level invocation, so MSBuild creates a SECOND project instance and runs a parallel build. Both instances write to the same bin\x64\Release\net10.0\Microsoft.Windows.SDK.NET.dll, so the assembly version embedded in AuthoringTest.dll's compile-time reference can disagree with the version .AOT.csproj's RAR resolves at link time -> NETSDK1148. Confirmed via binlog (cswinrt (13).binlog): two Windows.csproj instances, one with no special globals (Id=2095), another with _AuthoringTestSkipAotPublishConfig=true in its global property set (Id=2470). TreatAsLocalProperty on the <Project> element makes MSBuild treat the property as local within AuthoringTest's evaluation only - the gating Condition in Directory.Build.props still sees it, but it is NOT propagated as a global to AuthoringTest's ProjectReferences. Single Projections/Windows instance, no race, single SDK.NET version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thoringTest" This reverts commit 73e1cd9.
Real root cause of NETSDK1148 (from binlog cswinrt (13).binlog
ResolveAssemblyReferenceUnresolvedAssemblyConflicts entry):
Conflict: Microsoft.Windows.SDK.NET, Version=1.0.0.0, PublicKeyToken=null
(locally built from Projections/Windows.csproj)
vs Microsoft.Windows.SDK.NET, Version=10.0.17763.38,
PublicKeyToken=31bf3856ad364e35 (official signed)
Source of the official ref:
microsoft.web.webview2/1.0.3179.45/lib_manual/net8.0-windows10.0.17763.0/
Microsoft.Web.WebView2.Core.Projection.dll
WebView2.Core.Projection.dll was compiled against the official Windows SDK
projection assembly. AuthoringTest's csproj has <PackageReference> for
WebView2 with ExcludeAssets='all' which excludes WebView2 from AuthoringTest's
own compile, but does NOT block transitive flow (PrivateAssets default is
not 'all'). When .AOT.csproj resolves AuthoringTest's transitive package
graph, WebView2.Core.Projection.dll flows in, RAR cannot unify the two
SDK.NET versions, NETSDK1148 fires.
Add the same <PackageReference Microsoft.Web.WebView2 ExcludeAssets='all'>
at the .AOT.csproj level so this csproj also excludes WebView2 from its
compile/runtime closure.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
slnx's metaproj passes Targets='Build' to top-level projects regardless of DefaultTargets, so DefaultTargets='Publish' was ignored and ILC never ran on .AOT.csproj - confirmed via binlog. Add a target that runs Publish AfterTargets=Build so the AOT pipeline fires inside slnx-level Build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit facf401.
…Test3 AuthoringTest has a special Directory.Build.props block for Release|x64 that turns it into a self-contained AOT-publish executable (used by the single-component AOT scenario AuthoringConsumptionTest). Pulling it as a managed-only ProjectReference into the merged-AOT host required an opt-out global (_AuthoringTestSkipAotPublishConfig=true) to suppress that block, but the global propagated transitively, creating duplicate MSBuild project instances for transitive references and causing brittle build behavior locally (winmd path mismatch). Sidestep entirely: add AuthoringTest3, a plain CsWinRT component identical in shape to AuthoringTest2 (no AOT-publish block). The .AOT host now PRs AuthoringTest2 + AuthoringTest3. AuthoringTest stays untouched and continues to serve the single-component AOT test. Revert _AuthoringTestSkipAotPublishConfig gate on AuthoringTest's Directory.Build.props. Update tests/pch/manifests/vcxproj/slnx to reference AuthoringTest3 instead of AuthoringTest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AuthoringExportTypesGenerator.ShouldEmitNativeExports requires PublishAot=true to emit the unmanaged [UnmanagedCallersOnly] DllGetActivationFactory export. That property was exposed only via Microsoft.Windows.CsWinRT.Authoring.targets, which is imported only when CsWinRTComponent=true. A merged AOT host like AuthoringConsumptionTest2.AOT.csproj is NOT a component (it sets CsWinRTMergeReferencedActivationFactories=true but not CsWinRTComponent =true), so Authoring.targets isn't imported, PublishAot isn't visible to the source generator, ShouldEmitNativeExports() returns false, NativeExports.g.cs isn't generated, and the published native dll has no DllGetActivationFactory export. Activation fails at runtime with check_hresult throwing. Expose PublishAot unconditionally in the main CsWinRT.targets so the source generator sees it for both components and merged-aggregator hosts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AuthoringTest2/AuthoringTest3 are plain CsWinRT components (no AOT publish block in their Directory.Build.props), so their winmd output naturally lands at bin\<Platform>\<Config>\<TFM>\<assembly>.winmd (no RID subfolder). The old HintPath inherited the AuthoringTest pattern that expected the AOT publish RID-appended location, which fails locally where the VS build doesn't propagate RuntimeIdentifier through the PR Properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DefaultTargets='Publish' is honored only when the project is invoked with default targets (no explicit /t:). slnx via msbuild does that, but VS solution build passes Targets='Build' explicitly, so Publish never runs locally and the AOT pipeline doesn't fire. Hook Publish AfterTargets =Build so it runs in both invocation modes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Release|x64 uses the merged AOT host, not WinRT.Host. The CopyTestAssets target tries to copy WinRT.Host.dll which isn't built in the AOT-only config. Gate the same way AuthoringConsumptionTest does. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Activation crash root cause: the components' ABI.{Name}.ManagedExports.
GetActivationFactory bodies use [UnsafeAccessor] to delegate to a same
named type in WinRT.Component.dll (where the actual activation logic
with class-name -> ServerActivationFactory lookup lives). ILC's trimmer
doesn't follow that cross-assembly string-typed UnsafeAccessor reference,
so it drops the methods holding the activation logic. Verified by
inspecting the AOT.dll: GreeterServerActivationFactory class names
survive, but the string literals 'AuthoringTest2.Greeter' and
'AuthoringTest3.Calculator' (used inside the trimmed methods) do not.
DllGetActivationFactory then returns 0x80131534 (COR_E_TYPELOAD) at
activation -> cppwinrt's check_hresult throws.
Root the WinRT.Component assembly so all its types/methods survive.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 0a5fa4c.
…t for component-only references
The merged projection dll (WinRT.Projection.dll) is only produced when there are
non-component reference projections (3rd-party libs with [WindowsRuntimeReferenceAssembly]).
Components are projected into WinRT.Component.dll instead. Emitting
[TypeMapAssemblyTarget("WinRT.Projection")] when only components are referenced caused
WindowsRuntimeMarshallingInfo's static cctor to throw FileNotFoundException for the
missing assembly, surfacing as 0x80131534 / COR_E_TYPELOAD at every WinRT activation site
in merged-AOT consumers.
Fix: derive hasMergedProjection from non-SDK reference assemblies only, excluding
component assemblies. The combined list is still used for the per-assembly
PrivateProjections entries.
Also remove a stray EmitCompilerGeneratedFiles property from AuthoringConsumptionTest2.AOT.csproj.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sergio0694
reviewed
May 13, 2026
Comment on lines
+72
to
+75
| <!-- PublishAot is needed by AuthoringExportTypesGenerator.ShouldEmitNativeExports for the | ||
| merged-aggregator scenario (non-component project with CsWinRTMergeReferencedActivationFactories=true). | ||
| Authoring.targets exposes the same property but only imports when CsWinRTComponent=true. --> | ||
| <CompilerVisibleProperty Include="PublishAot" /> |
Member
There was a problem hiding this comment.
Super nit: leave a blank line before the comment and use the same style as the comment above (L37)
| /// initializer; consumers using a single component dll directly must call | ||
| /// <c>Assembly.SetEntryAssembly</c> themselves if they need TypeMap discovery rooted there. | ||
| /// </summary> | ||
| private static void WriteProjectionTypesInitializer(ProjectionGeneratorProcessingState processingState) |
Member
There was a problem hiding this comment.
I don't really like this one, is there some way we can drop module initializers entirely at least in some scenarios? That is, can we statically determine whether someone else will set the entry assembly already and skip this? For instance, is this needed in all of these scenarios?
- A standalone WinRT component published as NAOT
- An application with also a WinRT component, with .exe/.dll stub merging
Can we figure out a way to perhaps just scope this down to the mixed C++ scenarios that need it?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.