Skip to content
Merged
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
69 changes: 53 additions & 16 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class Generator : IDisposable
internal const string UnmanagedInteropSuffix = "_unmanaged";

internal static readonly SyntaxAnnotation IsRetValAnnotation = new SyntaxAnnotation("RetVal");
internal static readonly IdentifierNameSyntax NestedCOMInterfaceName = IdentifierName("Interface");

/// <summary>
/// A map of .NET interop structs to use, keyed by the native structs that should <em>not</em> be generated.
Expand Down Expand Up @@ -3411,7 +3412,7 @@ private ClassDeclarationSyntax DeclareComInterfaceFriendlyExtensionsClass()
private TypeDeclarationSyntax? DeclareInterface(TypeDefinitionHandle typeDefHandle, Context context)
{
TypeDefinition typeDef = this.Reader.GetTypeDefinition(typeDefHandle);
var baseTypes = new Stack<QualifiedTypeDefinitionHandle>();
var baseTypes = ImmutableStack.Create<QualifiedTypeDefinitionHandle>();
(Generator Generator, InterfaceImplementationHandle Handle) baseTypeHandle = (this, typeDef.GetInterfaceImplementations().SingleOrDefault());
while (!baseTypeHandle.Handle.IsNil)
{
Expand All @@ -3421,17 +3422,40 @@ private ClassDeclarationSyntax DeclareComInterfaceFriendlyExtensionsClass()
throw new GenerationFailedException("Failed to find base type.");
}

baseTypes.Push(baseTypeDefHandle);
baseTypes = baseTypes.Push(baseTypeDefHandle);
TypeDefinition baseType = baseTypeDefHandle.Reader.GetTypeDefinition(baseTypeDefHandle.DefinitionHandle);
baseTypeHandle = (baseTypeHandle.Generator, baseType.GetInterfaceImplementations().SingleOrDefault());
}

return !context.AllowMarshaling || this.IsNonCOMInterface(typeDef)
? this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context)
: this.DeclareInterfaceAsInterface(typeDef, baseTypes);
if (this.IsNonCOMInterface(typeDef))
{
// We cannot declare an interface that is not COM-compliant.
return this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context);
}

if (context.AllowMarshaling)
{
// Marshaling is allowed here, and generally. Just emit the interface.
return this.DeclareInterfaceAsInterface(typeDef, baseTypes, context);
}

// Marshaling of this interface is not allowed here. Emit the struct.
TypeDeclarationSyntax structDecl = this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context);
if (!this.options.AllowMarshaling)
{
// Marshaling isn't allowed over the entire compilation, so emit the interface nested under the struct so
// it can be implemented and enable CCW scenarios.
TypeDeclarationSyntax? ifaceDecl = this.DeclareInterfaceAsInterface(typeDef, baseTypes, context, interfaceAsSubtype: true);
if (ifaceDecl is not null)
{
structDecl = structDecl.AddMembers(ifaceDecl);
}
}

return structDecl;
}

private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle typeDefHandle, Stack<QualifiedTypeDefinitionHandle> baseTypes, Context context)
private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle typeDefHandle, ImmutableStack<QualifiedTypeDefinitionHandle> baseTypes, Context context)
{
TypeDefinition typeDef = this.Reader.GetTypeDefinition(typeDefHandle);
IdentifierNameSyntax ifaceName = IdentifierName(this.GetMangledIdentifier(this.Reader.GetString(typeDef.Name), context.AllowMarshaling, isManagedType: true));
Expand All @@ -3442,9 +3466,10 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type

// It is imperative that we generate methods for all base interfaces as well, ahead of any implemented by *this* interface.
var allMethods = new List<QualifiedMethodDefinitionHandle>();
while (baseTypes.Count > 0)
while (!baseTypes.IsEmpty)
{
QualifiedTypeDefinitionHandle qualifiedBaseType = baseTypes.Pop();
QualifiedTypeDefinitionHandle qualifiedBaseType = baseTypes.Peek();
baseTypes = baseTypes.Pop();
TypeDefinition baseType = qualifiedBaseType.Generator.Reader.GetTypeDefinition(qualifiedBaseType.DefinitionHandle);
allMethods.AddRange(baseType.GetMethods().Select(m => new QualifiedMethodDefinitionHandle(qualifiedBaseType.Generator, m)));
}
Expand Down Expand Up @@ -3570,15 +3595,17 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
return iface;
}

private TypeDeclarationSyntax? DeclareInterfaceAsInterface(TypeDefinition typeDef, Stack<QualifiedTypeDefinitionHandle> baseTypes)
private TypeDeclarationSyntax? DeclareInterfaceAsInterface(TypeDefinition typeDef, ImmutableStack<QualifiedTypeDefinitionHandle> baseTypes, Context context, bool interfaceAsSubtype = false)
{
if (this.Reader.StringComparer.Equals(typeDef.Name, "IUnknown") || this.Reader.StringComparer.Equals(typeDef.Name, "IDispatch"))
{
// We do not generate interfaces for these COM base types.
return null;
}

IdentifierNameSyntax ifaceName = IdentifierName(this.Reader.GetString(typeDef.Name));
IdentifierNameSyntax ifaceName = interfaceAsSubtype
? NestedCOMInterfaceName
: IdentifierName(this.Reader.GetString(typeDef.Name));
TypeSyntaxSettings typeSettings = this.comSignatureTypeSettings;

// It is imperative that we generate methods for all base interfaces as well, ahead of any implemented by *this* interface.
Expand All @@ -3587,9 +3614,10 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
bool foundIDispatch = false;
bool foundIInspectable = false;
var baseTypeSyntaxList = new List<BaseTypeSyntax>();
while (baseTypes.Count > 0)
while (!baseTypes.IsEmpty)
{
QualifiedTypeDefinitionHandle baseTypeHandle = baseTypes.Pop();
QualifiedTypeDefinitionHandle baseTypeHandle = baseTypes.Peek();
baseTypes = baseTypes.Pop();
TypeDefinition baseType = baseTypeHandle.Reader.GetTypeDefinition(baseTypeHandle.DefinitionHandle);
if (!foundIUnknown)
{
Expand All @@ -3612,8 +3640,16 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
}
else
{
baseTypeHandle.Generator.RequestInteropType(baseTypeHandle.DefinitionHandle, this.DefaultContext);
baseTypeSyntaxList.Add(SimpleBaseType(new HandleTypeHandleInfo(baseTypeHandle.Reader, baseTypeHandle.DefinitionHandle).ToTypeSyntax(this.comSignatureTypeSettings, null).Type));
baseTypeHandle.Generator.RequestInteropType(baseTypeHandle.DefinitionHandle, context);
TypeSyntax baseTypeSyntax = new HandleTypeHandleInfo(baseTypeHandle.Reader, baseTypeHandle.DefinitionHandle).ToTypeSyntax(this.comSignatureTypeSettings, null).Type;
if (interfaceAsSubtype)
{
baseTypeSyntax = QualifiedName(
baseTypeSyntax is PointerTypeSyntax baseTypePtr ? (NameSyntax)baseTypePtr.ElementType : (NameSyntax)baseTypeSyntax,
NestedCOMInterfaceName);
}

baseTypeSyntaxList.Add(SimpleBaseType(baseTypeSyntax));
allMethods.AddRange(baseType.GetMethods());
}
}
Expand Down Expand Up @@ -3645,7 +3681,8 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
TypeSyntax returnType = returnTypeDetails.Type;
AttributeSyntax? returnsAttribute = MarshalAs(returnTypeDetails.MarshalAsAttribute, returnTypeDetails.NativeArrayInfo);

bool preserveSig = returnType is not QualifiedNameSyntax { Right: { Identifier: { ValueText: "HRESULT" } } }
bool preserveSig = interfaceAsSubtype
|| returnType is not QualifiedNameSyntax { Right: { Identifier: { ValueText: "HRESULT" } } }
|| (methodDefinition.ImplAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig
|| this.options.ComInterop.PreserveSigMethods.Contains($"{ifaceName}.{methodName}")
|| this.options.ComInterop.PreserveSigMethods.Contains(ifaceName.ToString());
Expand Down Expand Up @@ -3697,7 +3734,7 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
methodDeclaration = this.AddApiDocumentation($"{ifaceName}.{methodName}", methodDeclaration);
members.Add(methodDeclaration);

NameSyntax declaringTypeName = HandleTypeHandleInfo.GetNestingQualifiedName(this, this.Reader, typeDef, hasUnmanagedSuffix: false);
NameSyntax declaringTypeName = HandleTypeHandleInfo.GetNestingQualifiedName(this, this.Reader, typeDef, hasUnmanagedSuffix: false, isInterfaceNestedInStruct: interfaceAsSubtype);
friendlyOverloads.AddRange(
this.DeclareFriendlyOverloads(methodDefinition, methodDeclaration, declaringTypeName, FriendlyOverloadOf.InterfaceMethod, this.injectedPInvokeHelperMethodsToFriendlyOverloadsExtensions));
}
Expand Down
27 changes: 21 additions & 6 deletions src/Microsoft.Windows.CsWin32/HandleTypeHandleInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
{
case HandleKind.TypeDefinition:
TypeDefinition td = this.reader.GetTypeDefinition((TypeDefinitionHandle)this.Handle);
nameSyntax = inputs.QualifyNames ? GetNestingQualifiedName(inputs.Generator, this.reader, td, hasUnmanagedSuffix) : IdentifierName(this.reader.GetString(td.Name) + simpleNameSuffix);
nameSyntax = inputs.QualifyNames ? GetNestingQualifiedName(inputs.Generator, this.reader, td, hasUnmanagedSuffix, isInterfaceNestedInStruct: false) : IdentifierName(this.reader.GetString(td.Name) + simpleNameSuffix);
isInterface = (td.Attributes & TypeAttributes.Interface) == TypeAttributes.Interface;
isNonCOMConformingInterface = isInterface && inputs.Generator?.IsNonCOMInterface(td) is true;
break;
Expand Down Expand Up @@ -205,9 +205,9 @@ private static bool TryMarshalAsObject(TypeSyntaxSettings inputs, string name, [
return false;
}

private static NameSyntax GetNestingQualifiedName(Generator? generator, MetadataReader reader, TypeDefinitionHandle handle, bool hasUnmanagedSuffix) => GetNestingQualifiedName(generator, reader, reader.GetTypeDefinition(handle), hasUnmanagedSuffix);
private static NameSyntax GetNestingQualifiedName(Generator? generator, MetadataReader reader, TypeDefinitionHandle handle, bool hasUnmanagedSuffix, bool isInterfaceNestedInStruct) => GetNestingQualifiedName(generator, reader, reader.GetTypeDefinition(handle), hasUnmanagedSuffix, isInterfaceNestedInStruct);

internal static NameSyntax GetNestingQualifiedName(Generator? generator, MetadataReader reader, TypeDefinition td, bool hasUnmanagedSuffix)
internal static NameSyntax GetNestingQualifiedName(Generator? generator, MetadataReader reader, TypeDefinition td, bool hasUnmanagedSuffix, bool isInterfaceNestedInStruct)
{
string simpleName = reader.GetString(td.Name);
if (hasUnmanagedSuffix)
Expand All @@ -216,9 +216,24 @@ internal static NameSyntax GetNestingQualifiedName(Generator? generator, Metadat
}

IdentifierNameSyntax name = IdentifierName(simpleName);
return td.GetDeclaringType() is { IsNil: false } nestingType
? QualifiedName(GetNestingQualifiedName(generator, reader, nestingType, hasUnmanagedSuffix), name)
: QualifiedName(ParseName(Generator.ReplaceCommonNamespaceWithAlias(generator, reader.GetString(td.Namespace))), name);
if (td.GetDeclaringType() is { IsNil: false } nestingType)
{
// This type is nested in another, so it is qualified by the parent type rather than by its own namespace.
return QualifiedName(GetNestingQualifiedName(generator, reader, nestingType, hasUnmanagedSuffix, isInterfaceNestedInStruct), name);
}
else
{
// This is not a nested type.
NameSyntax result = QualifiedName(ParseName(Generator.ReplaceCommonNamespaceWithAlias(generator, reader.GetString(td.Namespace))), name);

if (isInterfaceNestedInStruct)
{
// Such an interface has a special name, under the original one.
result = QualifiedName(result, Generator.NestedCOMInterfaceName);
}

return result;
}
}

private static NameSyntax GetNestingQualifiedName(Generator? generator, MetadataReader reader, TypeReferenceHandle handle, bool hasUnmanagedSuffix) => GetNestingQualifiedName(generator, reader, reader.GetTypeReference(handle), hasUnmanagedSuffix);
Expand Down