diff --git a/src/WinRT.Generator.Tasks/RunCsWinRTInteropGenerator.cs b/src/WinRT.Generator.Tasks/RunCsWinRTInteropGenerator.cs index 7537fd9ab..4bda5d364 100644 --- a/src/WinRT.Generator.Tasks/RunCsWinRTInteropGenerator.cs +++ b/src/WinRT.Generator.Tasks/RunCsWinRTInteropGenerator.cs @@ -44,13 +44,23 @@ public sealed class RunCsWinRTInteropGenerator : ToolTask public ITaskItem[]? OutputAssemblyPath { get; set; } /// - /// Gets or sets the path to the WinRT merged projection assembly. + /// Gets or sets the path to the WinRT Windows SDK projection assembly (i.e. WinRT.Sdk.Projection.dll). /// [Required] + public ITaskItem? WinRTSdkProjectionAssemblyPath { get; set; } + + /// + /// Gets or sets the path to the WinRT Windows SDK XAML projection assembly (i.e. WinRT.Sdk.Xaml.Projection.dll). + /// + public ITaskItem? WinRTSdkXamlProjectionAssemblyPath { get; set; } + + /// + /// Gets or sets the path to the WinRT merged projection assembly (i.e. WinRT.Projection.dll). + /// public ITaskItem? WinRTProjectionAssemblyPath { get; set; } /// - /// Gets or sets the path to the WinRT merged component assembly generated for authoring scenarios. + /// Gets or sets the path to the WinRT merged component assembly generated for authoring scenarios (i.e. WinRT.Component.dll). /// public ITaskItem? WinRTComponentAssemblyPath { get; set; } @@ -160,9 +170,9 @@ protected override bool ValidateParameters() return false; } - if (WinRTProjectionAssemblyPath is null) + if (WinRTSdkProjectionAssemblyPath is null) { - Log.LogWarning("Invalid 'WinRTProjectionAssemblyPath' input."); + Log.LogWarning("Invalid 'WinRTSdkProjectionAssemblyPath' input."); return false; } @@ -254,7 +264,9 @@ protected override string GenerateResponseFileCommands() AppendResponseFileCommand(args, "--reference-assembly-paths", referenceAssemblyPathsArg); AppendResponseFileCommand(args, "--implementation-assembly-paths", implementationAssemblyPathsArg); AppendResponseFileCommand(args, "--output-assembly-path", EffectiveOutputAssemblyItemSpec); - AppendResponseFileCommand(args, "--winrt-projection-assembly-path", WinRTProjectionAssemblyPath!.ItemSpec); + AppendResponseFileCommand(args, "--winrt-sdk-projection-assembly-path", WinRTSdkProjectionAssemblyPath!.ItemSpec); + AppendResponseFileOptionalCommand(args, "--winrt-sdk-xaml-projection-assembly-path", WinRTSdkXamlProjectionAssemblyPath?.ItemSpec); + AppendResponseFileOptionalCommand(args, "--winrt-projection-assembly-path", WinRTProjectionAssemblyPath?.ItemSpec); AppendResponseFileOptionalCommand(args, "--winrt-component-assembly-path", WinRTComponentAssemblyPath?.ItemSpec); AppendResponseFileCommand(args, "--generated-assembly-directory", InteropAssemblyDirectory!); AppendResponseFileOptionalCommand(args, "--debug-repro-directory", DebugReproDirectory); diff --git a/src/WinRT.Impl.Generator/Generation/ImplGenerator.cs b/src/WinRT.Impl.Generator/Generation/ImplGenerator.cs index 4696c8763..7e2f004af 100644 --- a/src/WinRT.Impl.Generator/Generation/ImplGenerator.cs +++ b/src/WinRT.Impl.Generator/Generation/ImplGenerator.cs @@ -12,6 +12,7 @@ using System.Security; using System.Security.Permissions; using System.Threading; +using AsmResolver; using AsmResolver.DotNet; using AsmResolver.PE.DotNet.StrongName; using ConsoleAppFramework; @@ -242,15 +243,38 @@ private static void EmitTypeForwards(ModuleDefinition inputModule, ModuleDefinit { try { - // We need an assembly reference for the merged projection .dll that will be generated. + // We need an assembly reference for the precompiled projection .dll for the Windows SDK. // The version doesn't matter here (as long as it's not '255.255.255.255'). The real .dll // will always have a version number equal or higher than this, so it will load correctly. + AssemblyReference sdkProjectionAssembly = new("WinRT.Sdk.Projection"u8, new Version(0, 0, 0, 0)) + { + PublicKeyOrToken = ImplValues.PublicKeyData, + HasPublicKey = true + }; + + // Similar as above, but for the precompiled XAML projection .dll for Windows SDK XAML types. + // This is only used when the option to use Windows UI Xaml projections is enabled. + AssemblyReference sdkXamlProjectionAssembly = new("WinRT.Sdk.Xaml.Projection"u8, new Version(0, 0, 0, 0)) + { + PublicKeyOrToken = ImplValues.PublicKeyData, + HasPublicKey = true + }; + + // Similar as above, but for the merged projection .dll for all other Windows Runtime types. + // Unlike the implementation .dll for the Windows SDK however, this .dll is created on the fly. AssemblyReference projectionAssembly = new("WinRT.Projection"u8, new Version(0, 0, 0, 0)) { PublicKeyOrToken = ImplValues.PublicKeyData, HasPublicKey = true }; + // Check if the input module is either of the Windows SDK reference assemblies. Types + // from the XAML assembly belong to the XAML projection .dll, while types from the SDK + // assembly belong to the standard SDK projection .dll. All other types are forwarded + // to the merged projection .dll, which is generated at final build time. + bool isSdkModule = inputModule.Assembly?.Name is Utf8String sdkName && sdkName.AsSpan().SequenceEqual("Microsoft.Windows.SDK.NET"u8); + bool isXamlModule = inputModule.Assembly?.Name is Utf8String xamlName && xamlName.AsSpan().SequenceEqual("Microsoft.Windows.UI.Xaml"u8); + foreach (TypeDefinition exportedType in inputModule.TopLevelTypes) { // We only need to forward public types @@ -259,9 +283,23 @@ private static void EmitTypeForwards(ModuleDefinition inputModule, ModuleDefinit continue; } + // Also make sure the type has a valid namespace, otherwise we can't handle it + if (exportedType.Namespace is null) + { + continue; + } + + // Determine the target assembly based on the declaring assembly of the current type. + // This matches the logic in 'cswinrtinteropgen' to figure out the right one. + AssemblyReference implementationAssembly = isXamlModule + ? sdkXamlProjectionAssembly + : isSdkModule + ? sdkProjectionAssembly + : projectionAssembly; + // Emit the type forwards for all public (projected) types implModule.ExportedTypes.Add(new ExportedType( - implementation: projectionAssembly.ImportWith(implModule.DefaultImporter), + implementation: implementationAssembly.ImportWith(implModule.DefaultImporter), ns: exportedType.Namespace, name: exportedType.Name) { diff --git a/src/WinRT.Interop.Generator/Builders/IgnoresAccessChecksToBuilder.cs b/src/WinRT.Interop.Generator/Builders/IgnoresAccessChecksToBuilder.cs index 1b73fd3e5..d00bffcd9 100644 --- a/src/WinRT.Interop.Generator/Builders/IgnoresAccessChecksToBuilder.cs +++ b/src/WinRT.Interop.Generator/Builders/IgnoresAccessChecksToBuilder.cs @@ -17,11 +17,17 @@ internal static partial class IgnoresAccessChecksToBuilder /// /// Defines all assembly attributes for target modules using the Windows TFM. /// + /// The for the Windows Runtime XAML projection assembly (i.e. WinRT.Sdk.Xaml.Projection.dll). + /// The for the Windows Runtime projection assembly (i.e. WinRT.Projection.dll). + /// The for the Windows Runtime component assembly (i.e. WinRT.Component.dll). /// The input set of reference path modules. /// The instance to use. /// The instance to use. /// The interop module being built. public static void AssemblyAttributes( + ModuleDefinition? winRTSdkXamlProjectionModule, + ModuleDefinition? winRTProjectionModule, + ModuleDefinition? winRTComponentModule, IEnumerable referencePathModules, InteropDefinitions interopDefinitions, InteropReferences interopReferences, @@ -43,8 +49,27 @@ public static void AssemblyAttributes( module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo(assemblyName, interopDefinitions, interopReferences)); } - // We also always add an attribute for 'WinRT.Projection', which is the merged Windows Runtime projection assembly. - // This assembly is generated at compile time by another task, and will not be present in the set of reference paths. - module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo("WinRT.Projection", interopDefinitions, interopReferences)); + // We also always add an attribute for 'WinRT.Sdk.Projection', which is the precompiled projection .dll for the Windows SDK + module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo("WinRT.Sdk.Projection", interopDefinitions, interopReferences)); + + // For 'WinRT.Sdk.Xaml.Projection', which is the precompiled projection .dll for Windows SDK XAML types, we only + // need the attribute if we do have that .dll being passed as input. That is only if XAML projections are enabled. + if (winRTSdkXamlProjectionModule is not null) + { + module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo("WinRT.Sdk.Xaml.Projection", interopDefinitions, interopReferences)); + } + + // For 'WinRT.Projection', which is the merged Windows Runtime projection assembly that is generated at final build time, + // we only need the attribute if we do have that .dll being passed as input. That is only if we have any 3rd party types. + if (winRTProjectionModule is not null) + { + module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo("WinRT.Projection", interopDefinitions, interopReferences)); + } + + // Similarly, we need an attribute for 'WinRT.Component' only if we reference some components and we do have this .dll + if (winRTComponentModule is not null) + { + module.Assembly!.CustomAttributes.Add(InteropCustomAttributeFactory.IgnoresAccessChecksTo("WinRT.Component", interopDefinitions, interopReferences)); + } } } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs index 949449d96..59835c960 100644 --- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs @@ -10,6 +10,8 @@ using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.References; +#pragma warning disable IDE0046 + namespace WindowsRuntime.InteropGenerator; /// @@ -61,6 +63,44 @@ public bool TryGetGuidAttribute(InteropReferences interopReferences, out Guid ii extension(ITypeDescriptor type) { + /// + /// Gets a value indicating whether the type is a projected Windows SDK type (not custom-mapped or manually-projected). + /// + public bool IsProjectedWindowsSdkType + { + get + { + // Types from 'Microsoft.Windows.SDK.NET.dll' belong to the SDK projection .dll. We check + // the declaring assembly name to reliably determine the origin of the type. We also optimize + // when an UTF8 value is available to avoid redundant UTF8 transcoding work. + if (type is TypeDefinition { DeclaringModule.Assembly.Name: Utf8String name }) + { + return name.AsSpan().SequenceEqual(InteropNames.WindowsSDKAssemblyNameUtf8); + } + + return false; + } + } + + /// + /// Gets a value indicating whether the type is a projected Windows SDK XAML type (from Microsoft.Windows.UI.Xaml.dll). + /// + public bool IsProjectedWindowsSdkXamlType + { + get + { + // Types from 'Microsoft.Windows.UI.Xaml.dll' belong to the XAML projection .dll. We check the + // declaring assembly name to reliably determine the origin of the type, as types from this + // assembly may span various 'Windows.*' sub-namespaces that can't be easily pattern-matched. + if (type is TypeDefinition { DeclaringModule.Assembly.Name: Utf8String name }) + { + return name.AsSpan().SequenceEqual(InteropNames.WindowsSDKXamlAssemblyNameUtf8); + } + + return false; + } + } + /// /// Checks whether an is some type. /// @@ -591,8 +631,15 @@ public TypeSignature GetAbiType(InteropReferences interopReferences) return interopReferences.AbiDateTimeOffset.ToValueTypeSignature(); } - // Otherwise, we can rely on the blittable type being defined in the same module under the 'ABI' namespace - return interopReferences.WinRTProjection.CreateTypeReference( + // Determine the right assembly reference for this projected type + AssemblyReference projectionAssembly = type.IsProjectedWindowsSdkXamlType + ? interopReferences.WinRTSdkXamlProjection + : type.IsProjectedWindowsSdkType + ? interopReferences.WinRTSdkProjection + : interopReferences.WinRTProjection; + + // For all types that get here, their ABI types will be in the right projection assembly, under the 'ABI' namespace + return projectionAssembly.CreateTypeReference( ns: (Utf8String)$"ABI.{typeDefinition.Namespace}", name: typeDefinition.Name!).ToValueTypeSignature(); } diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.DebugRepro.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.DebugRepro.cs index f3bf470e0..8055f5b65 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.DebugRepro.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.DebugRepro.cs @@ -76,6 +76,8 @@ private static string UnpackDebugRepro(string path, CancellationToken token) List referencePaths = []; List implementationPaths = []; string? outputAssemblyPath = null; + string? winRTSdkProjectionAssemblyPath = null; + string? winRTSdkXamlProjectionAssemblyPath = null; string? winRTProjectionAssemblyPath = null; string? winRTComponentAssemblyPath = null; @@ -142,7 +144,15 @@ private static string UnpackDebugRepro(string path, CancellationToken token) } // Also track the private implementation detail .dll-s (these are also in the set of references) - if (dllEntry.Name == args.WinRTProjectionAssemblyPath) + if (dllEntry.Name == args.WinRTSdkProjectionAssemblyPath) + { + winRTSdkProjectionAssemblyPath = destinationPath; + } + else if (args.WinRTSdkXamlProjectionAssemblyPath is not null && dllEntry.Name == args.WinRTSdkXamlProjectionAssemblyPath) + { + winRTSdkXamlProjectionAssemblyPath = destinationPath; + } + else if (args.WinRTProjectionAssemblyPath is not null && dllEntry.Name == args.WinRTProjectionAssemblyPath) { winRTProjectionAssemblyPath = destinationPath; } @@ -160,7 +170,9 @@ private static string UnpackDebugRepro(string path, CancellationToken token) ReferenceAssemblyPaths = [.. referencePaths], ImplementationAssemblyPaths = [.. implementationPaths], OutputAssemblyPath = outputAssemblyPath!, - WinRTProjectionAssemblyPath = winRTProjectionAssemblyPath!, + WinRTSdkProjectionAssemblyPath = winRTSdkProjectionAssemblyPath!, + WinRTSdkXamlProjectionAssemblyPath = winRTSdkXamlProjectionAssemblyPath, + WinRTProjectionAssemblyPath = winRTProjectionAssemblyPath, WinRTComponentAssemblyPath = winRTComponentAssemblyPath, GeneratedAssemblyDirectory = tempDirectory, UseWindowsUIXamlProjections = args.UseWindowsUIXamlProjections, @@ -226,7 +238,9 @@ private static void SaveDebugRepro(InteropGeneratorArgs args) // Hash and copy the well known assemblies we use as input string outputAssemblyHashedName = CopyHashedFileToDirectory(args.OutputAssemblyPath, tempDirectory, originalImplementationDllPaths, args.Token); - string winRTProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTProjectionAssemblyPath, implementationDirectory, originalImplementationDllPaths, args.Token); + string winRTSdkProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTSdkProjectionAssemblyPath, implementationDirectory, originalImplementationDllPaths, args.Token); + string? winRTSdkXamlProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTSdkXamlProjectionAssemblyPath, implementationDirectory, originalImplementationDllPaths, args.Token); + string? winRTProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTProjectionAssemblyPath, implementationDirectory, originalImplementationDllPaths, args.Token); string? winRTComponentAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTComponentAssemblyPath, implementationDirectory, originalImplementationDllPaths, args.Token); args.Token.ThrowIfCancellationRequested(); @@ -237,6 +251,8 @@ private static void SaveDebugRepro(InteropGeneratorArgs args) ReferenceAssemblyPaths = [.. updatedReferenceDllNames], ImplementationAssemblyPaths = [.. updatedImplementationDllNames], OutputAssemblyPath = outputAssemblyHashedName, + WinRTSdkProjectionAssemblyPath = winRTSdkProjectionAssemblyHashedName, + WinRTSdkXamlProjectionAssemblyPath = winRTSdkXamlProjectionAssemblyHashedName, WinRTProjectionAssemblyPath = winRTProjectionAssemblyHashedName, WinRTComponentAssemblyPath = winRTComponentAssemblyHashedName, GeneratedAssemblyDirectory = args.GeneratedAssemblyDirectory, diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs index 9321ba701..c44e9d482 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Discover.cs @@ -40,8 +40,9 @@ private static InteropGeneratorDiscoveryState Discover(InteropGeneratorArgs args // No additional parameters will be passed to later steps: all the info is in this object. InteropGeneratorDiscoveryState discoveryState = new() { AssemblyResolver = pathAssemblyResolver }; - // First, load the special 'WinRT.Projection.dll' and 'WinRT.Component.dll' modules (the latter is optional). - // These are necessary for surfacing some information needed to generate code, that is not present otherwise. + // First, load the special 'WinRT.Sdk.Projection.dll', 'WinRT.Sdk.Xaml.Projection.dll', 'WinRT.Projection.dll' + // and 'WinRT.Component.dll' modules (the last three are optional). These are necessary for surfacing some + // information needed to generate code, that is not present otherwise. LoadWinRTModules(args, discoveryState); try @@ -86,13 +87,33 @@ private static InteropGeneratorDiscoveryState Discover(InteropGeneratorArgs args /// The discovery state for this invocation. private static void LoadWinRTModules(InteropGeneratorArgs args, InteropGeneratorDiscoveryState discoveryState) { - // Load the 'WinRT.Projection.dll' module, this should always be available - ModuleDefinition winRTProjectionModule = ModuleDefinition.FromFile(args.WinRTProjectionAssemblyPath, ((PathAssemblyResolver)discoveryState.AssemblyResolver).ReaderParameters); + // Load the 'WinRT.Sdk.Projection.dll' module, this should always be available + ModuleDefinition winRTSdkProjectionModule = ModuleDefinition.FromFile(args.WinRTSdkProjectionAssemblyPath, ((PathAssemblyResolver)discoveryState.AssemblyResolver).ReaderParameters); - discoveryState.TrackWinRTProjectionModuleDefinition(winRTProjectionModule); + discoveryState.TrackWinRTSdkProjectionModuleDefinition(winRTSdkProjectionModule); args.Token.ThrowIfCancellationRequested(); + // Load the 'WinRT.Sdk.Xaml.Projection.dll' module, if available + if (args.WinRTSdkXamlProjectionAssemblyPath is not null) + { + ModuleDefinition winRTSdkXamlProjectionModule = ModuleDefinition.FromFile(args.WinRTSdkXamlProjectionAssemblyPath, ((PathAssemblyResolver)discoveryState.AssemblyResolver).ReaderParameters); + + discoveryState.TrackWinRTSdkXamlProjectionModuleDefinition(winRTSdkXamlProjectionModule); + + args.Token.ThrowIfCancellationRequested(); + } + + // Load the 'WinRT.Projection.dll' module, if available + if (args.WinRTProjectionAssemblyPath is not null) + { + ModuleDefinition winRTProjectionModule = ModuleDefinition.FromFile(args.WinRTProjectionAssemblyPath, ((PathAssemblyResolver)discoveryState.AssemblyResolver).ReaderParameters); + + discoveryState.TrackWinRTProjectionModuleDefinition(winRTProjectionModule); + + args.Token.ThrowIfCancellationRequested(); + } + // Load the 'WinRT.Component.dll' module, if available if (args.WinRTComponentAssemblyPath is not null) { @@ -115,10 +136,14 @@ private static void LoadAndProcessModule( { ReadOnlySpan fileName = Path.GetFileName(path.AsSpan()); - // Validate that the two possible private implementation detail .dll-s we expect have a matching path. These are: + // Validate that the possible private implementation detail .dll-s we expect have a matching path. These are: + // - 'WinRT.Sdk.Projection.dll': the precompiled projection assembly for the Windows SDK. + // - 'WinRT.Sdk.Xaml.Projection.dll': the precompiled projection assembly for the Windows SDK XAML types. // - 'WinRT.Projection.dll': the generated merged projection assembly. // - 'WinRT.Component.dll': the optional generated merged component assembly. - if ((fileName.SequenceEqual(InteropNames.WindowsRuntimeProjectionDllName) && path != args.WinRTProjectionAssemblyPath) || + if ((fileName.SequenceEqual(InteropNames.WindowsRuntimeSdkProjectionDllName) && path != args.WinRTSdkProjectionAssemblyPath) || + (fileName.SequenceEqual(InteropNames.WindowsRuntimeSdkXamlProjectionDllName) && path != args.WinRTSdkXamlProjectionAssemblyPath) || + (fileName.SequenceEqual(InteropNames.WindowsRuntimeProjectionDllName) && path != args.WinRTProjectionAssemblyPath) || (fileName.SequenceEqual(InteropNames.WindowsRuntimeComponentDllName) && path != args.WinRTComponentAssemblyPath)) { throw WellKnownInteropExceptions.ReservedDllOriginalPathMismatch(fileName.ToString()); @@ -127,7 +152,9 @@ private static void LoadAndProcessModule( // If the current module is one of those two .dll-s, we just skip it. They will be loaded separately (see above). // However since they're also passed in the reference set (as they need to be referenced by the app directly), // they will also show up here. This is intended, and it simplifies the targets (no need for them to filter items). - if (fileName.SequenceEqual(InteropNames.WindowsRuntimeProjectionDllName) || + if (fileName.SequenceEqual(InteropNames.WindowsRuntimeSdkProjectionDllName) || + fileName.SequenceEqual(InteropNames.WindowsRuntimeSdkXamlProjectionDllName) || + fileName.SequenceEqual(InteropNames.WindowsRuntimeProjectionDllName) || fileName.SequenceEqual(InteropNames.WindowsRuntimeComponentDllName)) { return; diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index ec7be4a35..82d5e7e23 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -47,7 +47,9 @@ private static void Emit(InteropGeneratorArgs args, InteropGeneratorDiscoverySta InteropReferences interopReferences = new(module.CorLibTypeFactory, windowsRuntimeModule, windowsFoundationModule); InteropDefinitions interopDefinitions = new( interopReferences: interopReferences, - windowsRuntimeProjectionModule: discoveryState.WinRTProjectionModuleDefinition!, + windowsRuntimeSdkProjectionModule: discoveryState.WinRTSdkProjectionModuleDefinition!, + windowsRuntimeSdkXamlProjectionModule: discoveryState.WinRTSdkXamlProjectionModuleDefinition, + windowsRuntimeProjectionModule: discoveryState.WinRTProjectionModuleDefinition, windowsRuntimeComponentModule: discoveryState.WinRTComponentModuleDefinition); args.Token.ThrowIfCancellationRequested(); @@ -2572,6 +2574,9 @@ private static void DefineIgnoresAccessChecksToAttributes( // Next, emit all the '[IgnoresAccessChecksTo]' attributes for each type IgnoresAccessChecksToBuilder.AssemblyAttributes( + winRTSdkXamlProjectionModule: discoveryState.WinRTSdkXamlProjectionModuleDefinition, + winRTProjectionModule: discoveryState.WinRTProjectionModuleDefinition, + winRTComponentModule: discoveryState.WinRTComponentModuleDefinition, referencePathModules: discoveryState.ModuleDefinitions.Values.OrderByFullyQualifiedName(), interopDefinitions: interopDefinitions, interopReferences: interopReferences, diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Formatting.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Formatting.cs index 85d79fe34..5fced287d 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Formatting.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Formatting.cs @@ -28,9 +28,23 @@ public string FormatToResponseFile() _ = builder.Append(' '); _ = builder.AppendLine(OutputAssemblyPath); - _ = builder.Append(GetCommandLineArgumentName(nameof(WinRTProjectionAssemblyPath))); + _ = builder.Append(GetCommandLineArgumentName(nameof(WinRTSdkProjectionAssemblyPath))); _ = builder.Append(' '); - _ = builder.AppendLine(WinRTProjectionAssemblyPath); + _ = builder.AppendLine(WinRTSdkProjectionAssemblyPath); + + if (WinRTSdkXamlProjectionAssemblyPath is not null) + { + _ = builder.Append(GetCommandLineArgumentName(nameof(WinRTSdkXamlProjectionAssemblyPath))); + _ = builder.Append(' '); + _ = builder.AppendLine(WinRTSdkXamlProjectionAssemblyPath); + } + + if (WinRTProjectionAssemblyPath is not null) + { + _ = builder.Append(GetCommandLineArgumentName(nameof(WinRTProjectionAssemblyPath))); + _ = builder.Append(' '); + _ = builder.AppendLine(WinRTProjectionAssemblyPath); + } if (WinRTComponentAssemblyPath is not null) { diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Parsing.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Parsing.cs index 9ebe6070e..89e4559ac 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Parsing.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.Parsing.cs @@ -100,7 +100,9 @@ private static InteropGeneratorArgs ParseFromResponseFile(string[] lines, Cancel ReferenceAssemblyPaths = GetStringArrayArgument(argsMap, nameof(ReferenceAssemblyPaths)), ImplementationAssemblyPaths = GetStringArrayArgument(argsMap, nameof(ImplementationAssemblyPaths)), OutputAssemblyPath = GetStringArgument(argsMap, nameof(OutputAssemblyPath)), - WinRTProjectionAssemblyPath = GetStringArgument(argsMap, nameof(WinRTProjectionAssemblyPath)), + WinRTSdkProjectionAssemblyPath = GetStringArgument(argsMap, nameof(WinRTSdkProjectionAssemblyPath)), + WinRTSdkXamlProjectionAssemblyPath = GetNullableStringArgument(argsMap, nameof(WinRTSdkXamlProjectionAssemblyPath)), + WinRTProjectionAssemblyPath = GetNullableStringArgument(argsMap, nameof(WinRTProjectionAssemblyPath)), WinRTComponentAssemblyPath = GetNullableStringArgument(argsMap, nameof(WinRTComponentAssemblyPath)), GeneratedAssemblyDirectory = GetStringArgument(argsMap, nameof(GeneratedAssemblyDirectory)), UseWindowsUIXamlProjections = GetBooleanArgument(argsMap, nameof(UseWindowsUIXamlProjections)), diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.cs index 391114981..0384a4f8c 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorArgs.cs @@ -23,9 +23,17 @@ internal sealed partial class InteropGeneratorArgs [CommandLineArgumentName("--output-assembly-path")] public required string OutputAssemblyPath { get; init; } + /// Gets the path of the WinRT.Sdk.Projection.dll assembly. + [CommandLineArgumentName("--winrt-sdk-projection-assembly-path")] + public required string WinRTSdkProjectionAssemblyPath { get; init; } + + /// Gets the path of the WinRT.Sdk.Xaml.Projection.dll assembly, if available. + [CommandLineArgumentName("--winrt-sdk-xaml-projection-assembly-path")] + public string? WinRTSdkXamlProjectionAssemblyPath { get; init; } + /// Gets the path of the WinRT.Projection.dll assembly. [CommandLineArgumentName("--winrt-projection-assembly-path")] - public required string WinRTProjectionAssemblyPath { get; init; } + public string? WinRTProjectionAssemblyPath { get; init; } /// Gets the path of the WinRT.Component.dll assembly, if available. [CommandLineArgumentName("--winrt-component-assembly-path")] diff --git a/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs b/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs index 99e1b3125..feed7e22e 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGeneratorDiscoveryState.cs @@ -19,6 +19,12 @@ internal sealed class InteropGeneratorDiscoveryState /// Backing field for . private readonly ConcurrentDictionary _moduleDefinitions = []; + /// Backing field for . + private ModuleDefinition? _winRTSdkProjectionModuleDefinition; + + /// Backing field for . + private ModuleDefinition? _winRTSdkXamlProjectionModuleDefinition; + /// Backing field for . private ModuleDefinition? _winRTProjectionModuleDefinition; @@ -116,6 +122,16 @@ internal sealed class InteropGeneratorDiscoveryState /// public IReadOnlyDictionary ModuleDefinitions => _moduleDefinitions; + /// + /// Gets the for WinRT.Sdk.Projection.dll. + /// + public ModuleDefinition? WinRTSdkProjectionModuleDefinition => _winRTSdkProjectionModuleDefinition; + + /// + /// Gets the for WinRT.Sdk.Xaml.Projection.dll. + /// + public ModuleDefinition? WinRTSdkXamlProjectionModuleDefinition => _winRTSdkXamlProjectionModuleDefinition; + /// /// Gets the for WinRT.Projection.dll. /// @@ -238,6 +254,30 @@ public void TrackModuleDefinition(string path, ModuleDefinition module) _ = _moduleDefinitions.TryAdd(path, module); } + /// + /// Tracks the WinRT.Sdk.Projection.dll loaded module definition. + /// + /// The loaded module. + [MemberNotNull(nameof(_winRTSdkProjectionModuleDefinition))] + public void TrackWinRTSdkProjectionModuleDefinition(ModuleDefinition module) + { + ThrowIfReadOnly(); + + _winRTSdkProjectionModuleDefinition = module; + } + + /// + /// Tracks the WinRT.Sdk.Xaml.Projection.dll loaded module definition. + /// + /// The loaded module. + [MemberNotNull(nameof(_winRTSdkXamlProjectionModuleDefinition))] + public void TrackWinRTSdkXamlProjectionModuleDefinition(ModuleDefinition module) + { + ThrowIfReadOnly(); + + _winRTSdkXamlProjectionModuleDefinition = module; + } + /// /// Tracks the WinRT.Projection.dll loaded module definition. /// diff --git a/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.cs b/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.cs index 3e163e299..367846a83 100644 --- a/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.cs +++ b/src/WinRT.Interop.Generator/Helpers/SignatureGenerator.cs @@ -158,12 +158,22 @@ private static bool TryGetIIDFromAttribute( return true; } - // For delegates, try to get the projected type from the projection .dll, as they will have the '[Guid]' attribute on them. + // For delegates, try to get the projected type from the right projection .dll, as they will have the '[Guid]' attribute on them. // These are only needed to generate signatures, so we hide them from the reference assemblies, as they're not useful there. - if (type.IsDelegate && - interopDefinitions.WindowsRuntimeProjectionModule.GetTopLevelTypesLookup().TryGetValue((type.Namespace, type.Name), out TypeDefinition? projectedType)) + if (type.IsDelegate) { - return projectedType.TryGetGuidAttribute(interopReferences, out iid); + // Determine the right implementation projection .dll to use for the lookup + ModuleDefinition? projectionModule = type.IsProjectedWindowsSdkXamlType + ? interopDefinitions.WindowsRuntimeSdkXamlProjectionModule + : type.IsProjectedWindowsSdkType + ? interopDefinitions.WindowsRuntimeSdkProjectionModule + : interopDefinitions.WindowsRuntimeProjectionModule; + + // Try to get the implementation type via a fast lookup, if we did get a valid projection module + if (projectionModule?.GetTopLevelTypesLookup().TryGetValue((type.Namespace, type.Name), out TypeDefinition? projectedType) is true) + { + return projectedType.TryGetGuidAttribute(interopReferences, out iid); + } } iid = Guid.Empty; @@ -186,8 +196,15 @@ private static bool TryGetDefaultInterfaceFromAttribute( InteropReferences interopReferences, [NotNullWhen(true)] out TypeSignature? defaultInterface) { + // Determine the right implementation projection .dll (see notes above) + ModuleDefinition? projectionModule = type.IsProjectedWindowsSdkXamlType + ? interopDefinitions.WindowsRuntimeSdkXamlProjectionModule + : type.IsProjectedWindowsSdkType + ? interopDefinitions.WindowsRuntimeSdkProjectionModule + : interopDefinitions.WindowsRuntimeProjectionModule; + // Tries to get the projected type from the projection .dll, as it will have the attribute - if (!interopDefinitions.WindowsRuntimeProjectionModule.GetTopLevelTypesLookup().TryGetValue((type.Namespace, type.Name), out TypeDefinition? projectedType)) + if (projectionModule?.GetTopLevelTypesLookup().TryGetValue((type.Namespace, type.Name), out TypeDefinition? projectedType) is not true) { defaultInterface = null; diff --git a/src/WinRT.Interop.Generator/References/InteropDefinitions.cs b/src/WinRT.Interop.Generator/References/InteropDefinitions.cs index 613f88b83..3fb8dd5c3 100644 --- a/src/WinRT.Interop.Generator/References/InteropDefinitions.cs +++ b/src/WinRT.Interop.Generator/References/InteropDefinitions.cs @@ -32,25 +32,41 @@ internal sealed class InteropDefinitions /// Creates a new instance. /// /// The instance to use. + /// The for the Windows Runtime projection assembly for the Windows SDK (i.e. WinRT.Sdk.Projection.dll). + /// The for the Windows Runtime projection assembly for the Windows SDK XAML types (i.e. WinRT.Sdk.Xaml.Projection.dll). /// The for the Windows Runtime projection assembly (i.e. WinRT.Projection.dll). /// The for the Windows Runtime component assembly (i.e. WinRT.Component.dll). public InteropDefinitions( InteropReferences interopReferences, - ModuleDefinition windowsRuntimeProjectionModule, + ModuleDefinition windowsRuntimeSdkProjectionModule, + ModuleDefinition? windowsRuntimeSdkXamlProjectionModule, + ModuleDefinition? windowsRuntimeProjectionModule, ModuleDefinition? windowsRuntimeComponentModule) { _interopReferences = interopReferences; _userDefinedInterfaceEntries = []; _szArrayInterfaceEntries = []; + WindowsRuntimeSdkProjectionModule = windowsRuntimeSdkProjectionModule; + WindowsRuntimeSdkXamlProjectionModule = windowsRuntimeSdkXamlProjectionModule; WindowsRuntimeProjectionModule = windowsRuntimeProjectionModule; WindowsRuntimeComponentModule = windowsRuntimeComponentModule; } + /// + /// Gets the for the Windows Runtime projection assembly for the Windows SDK (i.e. WinRT.Sdk.Projection.dll). + /// + public ModuleDefinition WindowsRuntimeSdkProjectionModule { get; } + + /// + /// Gets the for the Windows Runtime projection assembly for the Windows SDK XAML types (i.e. WinRT.Sdk.Xaml.Projection.dll). + /// + public ModuleDefinition? WindowsRuntimeSdkXamlProjectionModule { get; } + /// /// Gets the for the Windows Runtime projection assembly (i.e. WinRT.Projection.dll). /// - public ModuleDefinition WindowsRuntimeProjectionModule { get; } + public ModuleDefinition? WindowsRuntimeProjectionModule { get; } /// /// Gets the for the Windows Runtime component assembly (i.e. WinRT.Component.dll). diff --git a/src/WinRT.Interop.Generator/References/InteropNames.cs b/src/WinRT.Interop.Generator/References/InteropNames.cs index 49293ee8e..805774ff3 100644 --- a/src/WinRT.Interop.Generator/References/InteropNames.cs +++ b/src/WinRT.Interop.Generator/References/InteropNames.cs @@ -11,17 +11,27 @@ namespace WindowsRuntime.InteropGenerator.References; internal static class InteropNames { /// - /// The name of the generated interop .dll (i.e. WinRT.Interop.dll). + /// The name of the generated interop .dll (i.e. WinRT.Interop.dll). /// public const string WindowsRuntimeInteropDllName = "WinRT.Interop.dll"; /// - /// The name of the generated projection .dll (i.e. WinRT.Projection.dll). + /// The name of the precompiled projection .dll for the Windows SDK (i.e. WinRT.Sdk.Projection.dll). + /// + public const string WindowsRuntimeSdkProjectionDllName = "WinRT.Sdk.Projection.dll"; + + /// + /// The name of the precompiled projection .dll for the Windows SDK XAML types (i.e. WinRT.Sdk.Xaml.Projection.dll). + /// + public const string WindowsRuntimeSdkXamlProjectionDllName = "WinRT.Sdk.Xaml.Projection.dll"; + + /// + /// The name of the generated projection .dll (i.e. WinRT.Projection.dll). /// public const string WindowsRuntimeProjectionDllName = "WinRT.Projection.dll"; /// - /// The name of the generated component .dll (i.e. WinRT.Component.dll). + /// The name of the generated component .dll (i.e. WinRT.Component.dll). /// public const string WindowsRuntimeComponentDllName = "WinRT.Component.dll"; @@ -30,6 +40,21 @@ internal static class InteropNames /// public static ReadOnlySpan WindowsRuntimeInteropAssemblyNameUtf8 => "WinRT.Interop"u8; + /// + /// The assembly name of the precompiled projection for the Windows SDK (i.e. WinRT.Sdk.Projection). + /// + public static ReadOnlySpan WindowsRuntimeSdkProjectionAssemblyNameUtf8 => "WinRT.Sdk.Projection"u8; + + /// + /// The assembly name of the precompiled projection for the Windows SDK XAML types (i.e. WinRT.Sdk.Xaml.Projection). + /// + public static ReadOnlySpan WindowsRuntimeSdkXamlProjectionAssemblyNameUtf8 => "WinRT.Sdk.Xaml.Projection"u8; + + /// + /// The assembly name of the generated projection (i.e. WinRT.Projection). + /// + public static ReadOnlySpan WindowsRuntimeProjectionAssemblyNameUtf8 => "WinRT.Projection"u8; + /// /// The name of the generated interop .dll (i.e. WinRT.Interop.dll). /// @@ -45,8 +70,18 @@ internal static class InteropNames /// public static ReadOnlySpan WindowsSDKDllNameUtf8 => "Microsoft.Windows.SDK.NET.dll"u8; + /// + /// The assembly name of the Windows SDK projections (i.e. Microsoft.Windows.SDK.NET). + /// + public static ReadOnlySpan WindowsSDKAssemblyNameUtf8 => "Microsoft.Windows.SDK.NET"u8; + /// /// The name of the Windows SDK projections .dll. /// public static ReadOnlySpan WindowsSDKXamlDllNameUtf8 => "Microsoft.Windows.UI.Xaml.dll"u8; + + /// + /// The assembly name of the Windows SDK XAML projections (i.e. Microsoft.Windows.UI.Xaml). + /// + public static ReadOnlySpan WindowsSDKXamlAssemblyNameUtf8 => "Microsoft.Windows.UI.Xaml"u8; } \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index 728f558f0..7e820e96d 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -164,12 +164,32 @@ public InteropReferences( publicKey: false, publicKeyOrToken: WellKnownPublicKeyTokens.SystemThreading).Import(_corLibTypeFactory.CorLibScope); + /// + /// Gets the for WinRT.Sdk.Projection.dll. + /// + /// + public AssemblyReference WinRTSdkProjection => field ??= new AssemblyReference( + name: InteropNames.WindowsRuntimeSdkProjectionAssemblyNameUtf8, + version: new Version(0, 0, 0, 0), + publicKey: false, + publicKeyOrToken: default).Import(_corLibTypeFactory.CorLibScope); + + /// + /// Gets the for WinRT.Sdk.Xaml.Projection.dll. + /// + /// + public AssemblyReference WinRTSdkXamlProjection => field ??= new AssemblyReference( + name: InteropNames.WindowsRuntimeSdkXamlProjectionAssemblyNameUtf8, + version: new Version(0, 0, 0, 0), + publicKey: false, + publicKeyOrToken: default).Import(_corLibTypeFactory.CorLibScope); + /// /// Gets the for WinRT.Projection.dll. /// /// public AssemblyReference WinRTProjection => field ??= new AssemblyReference( - name: "WinRT.Projection"u8, + name: InteropNames.WindowsRuntimeProjectionAssemblyNameUtf8, version: new Version(0, 0, 0, 0), publicKey: false, publicKeyOrToken: default).Import(_corLibTypeFactory.CorLibScope); diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs b/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs index 995151dae..e7288751f 100644 --- a/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs +++ b/src/WinRT.Interop.Generator/Resolvers/InteropImplTypeResolver.cs @@ -78,15 +78,22 @@ public static (IMethodDefOrRef get_IID, IMethodDefOrRef get_Vtable) GetProjected TypeDefinition type, InteropReferences interopReferences) { - // Finally, we have the base scenario of simple non-generic projected Windows Runtime - // interface types. In this case, the marshalling code will be in the merged projection. - TypeReference ImplTypeReference = interopReferences.WinRTProjection.CreateTypeReference($"ABI.{type.Namespace}", $"{type.Name}Impl"); + // Determine the right assembly reference for this projected type + AssemblyReference projectionAssembly = type.IsProjectedWindowsSdkXamlType + ? interopReferences.WinRTSdkXamlProjection + : type.IsProjectedWindowsSdkType + ? interopReferences.WinRTSdkProjection + : interopReferences.WinRTProjection; + + // Finally, we have the base scenario of simple non-generic projected Windows Runtime interface types. + // Those will have the marshalling code in the right implementation projection .dll that we found above. + TypeReference ImplTypeReference = projectionAssembly.CreateTypeReference($"ABI.{type.Namespace}", $"{type.Name}Impl"); MemberReference get_VtableMethod = ImplTypeReference.CreateMemberReference("get_Vtable"u8, MethodSignature.CreateStatic(interopReferences.CorLibTypeFactory.IntPtr)); // For normal projected types, the IID is in the generated 'InterfaceIIDs' type in the merged projection string get_IIDMethodName = $"get_IID_{type.FullName.Replace('.', '_')}"; TypeSignature get_IIDMethodReturnType = WellKnownTypeSignatureFactory.InGuid(interopReferences); - TypeReference interfaceIIDsTypeReference = interopReferences.WinRTProjection.CreateTypeReference("ABI"u8, "InterfaceIIDs"u8); + TypeReference interfaceIIDsTypeReference = projectionAssembly.CreateTypeReference("ABI"u8, "InterfaceIIDs"u8); MemberReference get_IIDMethod = interfaceIIDsTypeReference.CreateMemberReference(get_IIDMethodName, MethodSignature.CreateStatic(get_IIDMethodReturnType)); // Return the pair of methods from the ABI type in the declaring assembly for the type diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs index 65b8c385d..5c22635ce 100644 --- a/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs +++ b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs @@ -68,8 +68,15 @@ public static InteropMarshallerType GetMarshallerType( } else { - // In all other cases, the marshaller type will be in the merged projection. - ITypeDefOrRef marshallerType = interopReferences.WinRTProjection.CreateTypeReference( + // Determine the right assembly reference for this projected type + AssemblyReference projectionAssembly = type.IsProjectedWindowsSdkXamlType + ? interopReferences.WinRTSdkXamlProjection + : type.IsProjectedWindowsSdkType + ? interopReferences.WinRTSdkProjection + : interopReferences.WinRTProjection; + + // In all other cases, the marshaller type will be in the right merged projection + ITypeDefOrRef marshallerType = projectionAssembly.CreateTypeReference( ns: $"ABI.{type.Namespace}", name: $"{type.Name}Marshaller");