Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 17 additions & 5 deletions src/WinRT.Generator.Tasks/RunCsWinRTInteropGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,23 @@ public sealed class RunCsWinRTInteropGenerator : ToolTask
public ITaskItem[]? OutputAssemblyPath { get; set; }

/// <summary>
/// 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. <c>WinRT.Sdk.Projection.dll</c>).
/// </summary>
[Required]
public ITaskItem? WinRTSdkProjectionAssemblyPath { get; set; }

/// <summary>
/// Gets or sets the path to the WinRT Windows SDK XAML projection assembly (i.e. <c>WinRT.Sdk.Xaml.Projection.dll</c>).
/// </summary>
public ITaskItem? WinRTSdkXamlProjectionAssemblyPath { get; set; }

/// <summary>
/// Gets or sets the path to the WinRT merged projection assembly (i.e. <c>WinRT.Projection.dll</c>).
/// </summary>
public ITaskItem? WinRTProjectionAssemblyPath { get; set; }

/// <summary>
/// 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. <c>WinRT.Component.dll</c>).
/// </summary>
public ITaskItem? WinRTComponentAssemblyPath { get; set; }

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
42 changes: 40 additions & 2 deletions src/WinRT.Impl.Generator/Generation/ImplGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ internal static partial class IgnoresAccessChecksToBuilder
/// <summary>
/// Defines all assembly attributes for target modules using the Windows TFM.
/// </summary>
/// <param name="winRTSdkXamlProjectionModule">The <see cref="ModuleDefinition"/> for the Windows Runtime XAML projection assembly (i.e. <c>WinRT.Sdk.Xaml.Projection.dll</c>).</param>
/// <param name="winRTProjectionModule">The <see cref="ModuleDefinition"/> for the Windows Runtime projection assembly (i.e. <c>WinRT.Projection.dll</c>).</param>
/// <param name="winRTComponentModule">The <see cref="ModuleDefinition"/> for the Windows Runtime component assembly (i.e. <c>WinRT.Component.dll</c>).</param>
/// <param name="referencePathModules">The input set of reference path modules.</param>
/// <param name="interopDefinitions">The <see cref="InteropDefinitions"/> instance to use.</param>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <param name="module">The interop module being built.</param>
public static void AssemblyAttributes(
ModuleDefinition? winRTSdkXamlProjectionModule,
ModuleDefinition? winRTProjectionModule,
ModuleDefinition? winRTComponentModule,
IEnumerable<ModuleDefinition> referencePathModules,
InteropDefinitions interopDefinitions,
InteropReferences interopReferences,
Expand All @@ -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));
}
}
}
49 changes: 47 additions & 2 deletions src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,44 @@ public bool TryGetGuidAttribute(InteropReferences interopReferences, out Guid ii

extension(ITypeDescriptor type)
{
/// <summary>
/// Gets a value indicating whether the type is a projected Windows SDK type (not custom-mapped or manually-projected).
/// </summary>
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 { Module.Assembly.Name: Utf8String name })
{
return name.AsSpan().SequenceEqual(InteropNames.WindowsSDKAssemblyNameUtf8);
}

return false;
}
}

/// <summary>
/// Gets a value indicating whether the type is a projected Windows SDK XAML type (from <c>Microsoft.Windows.UI.Xaml.dll</c>).
/// </summary>
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 { Module.Assembly.Name: Utf8String name })
{
return name.AsSpan().SequenceEqual(InteropNames.WindowsSDKXamlAssemblyNameUtf8);
}

return false;
}
}

/// <summary>
/// Checks whether an <see cref="ITypeDescriptor"/> is some <see cref="Guid"/> type.
/// </summary>
Expand Down Expand Up @@ -590,8 +628,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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ private static string UnpackDebugRepro(string path, CancellationToken token)
List<string> referencePaths = [];
List<string> implementationPaths = [];
string? outputAssemblyPath = null;
string? winRTSdkProjectionAssemblyHashedName = null;
string? winRTSdkXamlProjectionAssemblyHashedName = null;
string? winRTProjectionAssemblyHashedName = null;
string? winRTComponentAssemblyHashedName = null;

Expand Down Expand Up @@ -109,7 +111,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)
{
winRTSdkProjectionAssemblyHashedName = destinationPath;
}
else if (args.WinRTSdkXamlProjectionAssemblyPath is not null && dllEntry.Name == args.WinRTSdkXamlProjectionAssemblyPath)
{
winRTSdkXamlProjectionAssemblyHashedName = destinationPath;
}
else if (args.WinRTProjectionAssemblyPath is not null && dllEntry.Name == args.WinRTProjectionAssemblyPath)
{
winRTProjectionAssemblyHashedName = destinationPath;
}
Expand All @@ -127,7 +137,9 @@ private static string UnpackDebugRepro(string path, CancellationToken token)
ReferenceAssemblyPaths = [.. referencePaths],
ImplementationAssemblyPaths = [.. implementationPaths],
OutputAssemblyPath = outputAssemblyPath!,
WinRTProjectionAssemblyPath = winRTProjectionAssemblyHashedName!,
WinRTSdkProjectionAssemblyPath = winRTSdkProjectionAssemblyHashedName!,
WinRTSdkXamlProjectionAssemblyPath = winRTSdkXamlProjectionAssemblyHashedName,
WinRTProjectionAssemblyPath = winRTProjectionAssemblyHashedName,
WinRTComponentAssemblyPath = winRTComponentAssemblyHashedName,
GeneratedAssemblyDirectory = tempDirectory,
UseWindowsUIXamlProjections = args.UseWindowsUIXamlProjections,
Expand Down Expand Up @@ -192,7 +204,9 @@ private static void SaveDebugRepro(InteropGeneratorArgs args)

// Hash and copy the well known assemblies we use as input
string outputAssemblyHashedName = CopyHashedFileToDirectory(args.OutputAssemblyPath, tempDirectory, originalPaths, args.Token);
string winRTProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTProjectionAssemblyPath, tempDirectory, originalPaths, args.Token);
string winRTSdkProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTSdkProjectionAssemblyPath, tempDirectory, originalPaths, args.Token);
string? winRTSdkXamlProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTSdkXamlProjectionAssemblyPath, tempDirectory, originalPaths, args.Token);
string? winRTProjectionAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTProjectionAssemblyPath, tempDirectory, originalPaths, args.Token);
string? winRTComponentAssemblyHashedName = CopyHashedFileToDirectory(args.WinRTComponentAssemblyPath, tempDirectory, originalPaths, args.Token);

args.Token.ThrowIfCancellationRequested();
Expand All @@ -203,6 +217,8 @@ private static void SaveDebugRepro(InteropGeneratorArgs args)
ReferenceAssemblyPaths = [.. updatedReferenceDllNames],
ImplementationAssemblyPaths = [.. updatedImplementationDllNames],
OutputAssemblyPath = outputAssemblyHashedName,
WinRTSdkProjectionAssemblyPath = winRTSdkProjectionAssemblyHashedName,
WinRTSdkXamlProjectionAssemblyPath = winRTSdkXamlProjectionAssemblyHashedName,
WinRTProjectionAssemblyPath = winRTProjectionAssemblyHashedName,
WinRTComponentAssemblyPath = winRTComponentAssemblyHashedName,
GeneratedAssemblyDirectory = args.GeneratedAssemblyDirectory,
Expand Down
Loading
Loading