Skip to content

Commit 271f6d4

Browse files
committed
Declare COM interface within the COM struct when allowMarshaling is false
1 parent 471a4d7 commit 271f6d4

File tree

2 files changed

+74
-22
lines changed

2 files changed

+74
-22
lines changed

src/Microsoft.Windows.CsWin32/Generator.cs

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class Generator : IDisposable
3737
internal const string UnmanagedInteropSuffix = "_unmanaged";
3838

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

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

3424-
baseTypes.Push(baseTypeDefHandle);
3425+
baseTypes = baseTypes.Push(baseTypeDefHandle);
34253426
TypeDefinition baseType = baseTypeDefHandle.Reader.GetTypeDefinition(baseTypeDefHandle.DefinitionHandle);
34263427
baseTypeHandle = (baseTypeHandle.Generator, baseType.GetInterfaceImplementations().SingleOrDefault());
34273428
}
34283429

3429-
return !context.AllowMarshaling || this.IsNonCOMInterface(typeDef)
3430-
? this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context)
3431-
: this.DeclareInterfaceAsInterface(typeDef, baseTypes);
3430+
if (this.IsNonCOMInterface(typeDef))
3431+
{
3432+
// We cannot declare an interface that is not COM-compliant.
3433+
return this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context);
3434+
}
3435+
3436+
if (context.AllowMarshaling)
3437+
{
3438+
// Marshaling is allowed here, and generally. Just emit the interface.
3439+
return this.DeclareInterfaceAsInterface(typeDef, baseTypes, context);
3440+
}
3441+
3442+
// Marshaling of this interface is not allowed here. Emit the struct.
3443+
TypeDeclarationSyntax structDecl = this.DeclareInterfaceAsStruct(typeDefHandle, baseTypes, context);
3444+
if (!this.options.AllowMarshaling)
3445+
{
3446+
// Marshaling isn't allowed over the entire compilation, so emit the interface nested under the struct so
3447+
// it can be implemented and enable CCW scenarios.
3448+
TypeDeclarationSyntax? ifaceDecl = this.DeclareInterfaceAsInterface(typeDef, baseTypes, context, interfaceAsSubtype: true);
3449+
if (ifaceDecl is not null)
3450+
{
3451+
structDecl = structDecl.AddMembers(ifaceDecl);
3452+
}
3453+
}
3454+
3455+
return structDecl;
34323456
}
34333457

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

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

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

3581-
IdentifierNameSyntax ifaceName = IdentifierName(this.Reader.GetString(typeDef.Name));
3606+
IdentifierNameSyntax ifaceName = interfaceAsSubtype
3607+
? NestedCOMInterfaceName
3608+
: IdentifierName(this.Reader.GetString(typeDef.Name));
35823609
TypeSyntaxSettings typeSettings = this.comSignatureTypeSettings;
35833610

35843611
// It is imperative that we generate methods for all base interfaces as well, ahead of any implemented by *this* interface.
@@ -3587,9 +3614,10 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
35873614
bool foundIDispatch = false;
35883615
bool foundIInspectable = false;
35893616
var baseTypeSyntaxList = new List<BaseTypeSyntax>();
3590-
while (baseTypes.Count > 0)
3617+
while (!baseTypes.IsEmpty)
35913618
{
3592-
QualifiedTypeDefinitionHandle baseTypeHandle = baseTypes.Pop();
3619+
QualifiedTypeDefinitionHandle baseTypeHandle = baseTypes.Peek();
3620+
baseTypes = baseTypes.Pop();
35933621
TypeDefinition baseType = baseTypeHandle.Reader.GetTypeDefinition(baseTypeHandle.DefinitionHandle);
35943622
if (!foundIUnknown)
35953623
{
@@ -3612,8 +3640,16 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
36123640
}
36133641
else
36143642
{
3615-
baseTypeHandle.Generator.RequestInteropType(baseTypeHandle.DefinitionHandle, this.DefaultContext);
3616-
baseTypeSyntaxList.Add(SimpleBaseType(new HandleTypeHandleInfo(baseTypeHandle.Reader, baseTypeHandle.DefinitionHandle).ToTypeSyntax(this.comSignatureTypeSettings, null).Type));
3643+
baseTypeHandle.Generator.RequestInteropType(baseTypeHandle.DefinitionHandle, context);
3644+
TypeSyntax baseTypeSyntax = new HandleTypeHandleInfo(baseTypeHandle.Reader, baseTypeHandle.DefinitionHandle).ToTypeSyntax(this.comSignatureTypeSettings, null).Type;
3645+
if (interfaceAsSubtype)
3646+
{
3647+
baseTypeSyntax = QualifiedName(
3648+
baseTypeSyntax is PointerTypeSyntax baseTypePtr ? (NameSyntax)baseTypePtr.ElementType : (NameSyntax)baseTypeSyntax,
3649+
NestedCOMInterfaceName);
3650+
}
3651+
3652+
baseTypeSyntaxList.Add(SimpleBaseType(baseTypeSyntax));
36173653
allMethods.AddRange(baseType.GetMethods());
36183654
}
36193655
}
@@ -3645,7 +3681,8 @@ private TypeDeclarationSyntax DeclareInterfaceAsStruct(TypeDefinitionHandle type
36453681
TypeSyntax returnType = returnTypeDetails.Type;
36463682
AttributeSyntax? returnsAttribute = MarshalAs(returnTypeDetails.MarshalAsAttribute, returnTypeDetails.NativeArrayInfo);
36473683

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

3700-
NameSyntax declaringTypeName = HandleTypeHandleInfo.GetNestingQualifiedName(this, this.Reader, typeDef, hasUnmanagedSuffix: false);
3737+
NameSyntax declaringTypeName = HandleTypeHandleInfo.GetNestingQualifiedName(this, this.Reader, typeDef, hasUnmanagedSuffix: false, isInterfaceNestedInStruct: interfaceAsSubtype);
37013738
friendlyOverloads.AddRange(
37023739
this.DeclareFriendlyOverloads(methodDefinition, methodDeclaration, declaringTypeName, FriendlyOverloadOf.InterfaceMethod, this.injectedPInvokeHelperMethodsToFriendlyOverloadsExtensions));
37033740
}

src/Microsoft.Windows.CsWin32/HandleTypeHandleInfo.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ internal override TypeSyntaxAndMarshaling ToTypeSyntax(TypeSyntaxSettings inputs
4646
{
4747
case HandleKind.TypeDefinition:
4848
TypeDefinition td = this.reader.GetTypeDefinition((TypeDefinitionHandle)this.Handle);
49-
nameSyntax = inputs.QualifyNames ? GetNestingQualifiedName(inputs.Generator, this.reader, td, hasUnmanagedSuffix) : IdentifierName(this.reader.GetString(td.Name) + simpleNameSuffix);
49+
nameSyntax = inputs.QualifyNames ? GetNestingQualifiedName(inputs.Generator, this.reader, td, hasUnmanagedSuffix, isInterfaceNestedInStruct: false) : IdentifierName(this.reader.GetString(td.Name) + simpleNameSuffix);
5050
isInterface = (td.Attributes & TypeAttributes.Interface) == TypeAttributes.Interface;
5151
isNonCOMConformingInterface = isInterface && inputs.Generator?.IsNonCOMInterface(td) is true;
5252
break;
@@ -205,9 +205,9 @@ private static bool TryMarshalAsObject(TypeSyntaxSettings inputs, string name, [
205205
return false;
206206
}
207207

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

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

218218
IdentifierNameSyntax name = IdentifierName(simpleName);
219-
return td.GetDeclaringType() is { IsNil: false } nestingType
220-
? QualifiedName(GetNestingQualifiedName(generator, reader, nestingType, hasUnmanagedSuffix), name)
221-
: QualifiedName(ParseName(Generator.ReplaceCommonNamespaceWithAlias(generator, reader.GetString(td.Namespace))), name);
219+
if (td.GetDeclaringType() is { IsNil: false } nestingType)
220+
{
221+
// This type is nested in another, so it is qualified by the parent type rather than by its own namespace.
222+
return QualifiedName(GetNestingQualifiedName(generator, reader, nestingType, hasUnmanagedSuffix, isInterfaceNestedInStruct), name);
223+
}
224+
else
225+
{
226+
// This is not a nested type.
227+
NameSyntax result = QualifiedName(ParseName(Generator.ReplaceCommonNamespaceWithAlias(generator, reader.GetString(td.Namespace))), name);
228+
229+
if (isInterfaceNestedInStruct)
230+
{
231+
// Such an interface has a special name, under the original one.
232+
result = QualifiedName(result, Generator.NestedCOMInterfaceName);
233+
}
234+
235+
return result;
236+
}
222237
}
223238

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

0 commit comments

Comments
 (0)