Skip to content

Add custom exception handlers to generated COM wrappers #114828

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return new ComMethodContext(
data.Method,
data.OwningInterface,
CalculateStubInformation(data.Method.MethodInfo.Syntax, symbolMap[data.Method.MethodInfo], data.Method.Index, env, data.OwningInterface.Info.Type, ct));
CalculateStubInformation(data.Method.MethodInfo.Syntax, symbolMap[data.Method.MethodInfo], data.Method.Index, env, data.OwningInterface.Info.Type, data.OwningInterface.Info.ExceptionToUnmanagedMarshallerQualifiedName, ct));
}).WithTrackingName(StepNames.CalculateStubInformation);

var interfaceAndMethodsContexts = comMethodContexts
Expand Down Expand Up @@ -245,7 +245,7 @@ private static bool IsHResultLikeType(ManagedTypeInfo type)
|| typeName.Equals("hresult", StringComparison.OrdinalIgnoreCase);
}

private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ManagedTypeInfo owningInterface, CancellationToken ct)
private static IncrementalMethodStubGenerationContext CalculateStubInformation(MethodDeclarationSyntax syntax, IMethodSymbol symbol, int index, StubEnvironment environment, ManagedTypeInfo owningInterface, string? exceptionToUnmanagedMarshallerQualifiedName, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
INamedTypeSymbol? lcidConversionAttrType = environment.LcidConversionAttrType;
Expand Down Expand Up @@ -396,7 +396,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M
locations,
callConv.ToSequenceEqualImmutableArray(SyntaxEquivalentComparer.Instance),
virtualMethodIndexData,
new ComExceptionMarshalling(),
new ComExceptionMarshalling(exceptionToUnmanagedMarshallerQualifiedName),
environment.EnvironmentFlags,
owningInterface,
declaringType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal sealed record ComInterfaceInfo
public ComInterfaceOptions Options { get; init; }
public Location DiagnosticLocation { get; init; }
public bool IsExternallyDefined { get; init; }
public string? ExceptionToUnmanagedMarshallerQualifiedName { get; init; }

private ComInterfaceInfo(
ManagedTypeInfo type,
Expand All @@ -39,7 +40,8 @@ private ComInterfaceInfo(
ContainingSyntax containingSyntax,
Guid interfaceId,
ComInterfaceOptions options,
Location diagnosticLocation)
Location diagnosticLocation,
string? exceptionToUnmanagedMarshallerFullyQualifiedName)
{
Type = type;
ThisInterfaceKey = thisInterfaceKey;
Expand All @@ -50,6 +52,7 @@ private ComInterfaceInfo(
InterfaceId = interfaceId;
Options = options;
DiagnosticLocation = diagnosticLocation;
ExceptionToUnmanagedMarshallerQualifiedName = exceptionToUnmanagedMarshallerFullyQualifiedName;
}

public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceDeclarationSyntax syntax, StubEnvironment env, CancellationToken _)
Expand Down Expand Up @@ -94,6 +97,9 @@ public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceD
if (!OptionsAreValid(symbol, syntax, interfaceAttributeData, baseAttributeData, out DiagnosticInfo? optionsDiagnostic))
return DiagnosticOrInterfaceInfo.From(optionsDiagnostic);

if (!ExceptionToUnmanagedMarshallerIsValid(syntax, interfaceAttributeData, out string? exceptionToUnmanagedMarshallerFullyQualifiedName, out DiagnosticInfo? exceptionToUnmanagedMarshallerDiagnostic))
return DiagnosticOrInterfaceInfo.From(exceptionToUnmanagedMarshallerDiagnostic);

InterfaceInfo info = (
new ComInterfaceInfo(
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol),
Expand All @@ -104,7 +110,8 @@ public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceD
new ContainingSyntax(syntax.Modifiers, syntax.Kind(), syntax.Identifier, syntax.TypeParameterList),
guid ?? Guid.Empty,
interfaceAttributeData.Options,
syntax.Identifier.GetLocation()),
syntax.Identifier.GetLocation(),
exceptionToUnmanagedMarshallerFullyQualifiedName),
symbol);

// Now that we've validated all of our requirements, we will check for some non-blocking scenarios
Expand Down Expand Up @@ -138,6 +145,11 @@ public static DiagnosticOrInterfaceInfo From(INamedTypeSymbol symbol, InterfaceD
return DiagnosticOrInterfaceInfo.From(info);
}

internal static readonly SymbolDisplayFormat QualifiedNameOnlyFormat =
new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);

public static ImmutableArray<InterfaceInfo> CreateInterfaceInfoForBaseInterfacesInOtherCompilations(
INamedTypeSymbol symbol)
{
Expand All @@ -153,6 +165,7 @@ public static ImmutableArray<InterfaceInfo> CreateInterfaceInfoForBaseInterfaces
var thisSymbol = baseSymbol;
TryGetBaseComInterface(thisSymbol, null, out baseSymbol, out _);
var interfaceAttributeData = GeneratedComInterfaceCompilationData.GetAttributeDataFromInterfaceSymbol(thisSymbol);
string? exceptionToUnmanagedMarshallerQualifiedName = interfaceAttributeData.ExceptionToUnmanagedMarshaller?.ToDisplayString(QualifiedNameOnlyFormat);
builder.Add((
new ComInterfaceInfo(
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(thisSymbol),
Expand All @@ -163,7 +176,8 @@ public static ImmutableArray<InterfaceInfo> CreateInterfaceInfoForBaseInterfaces
default,
Guid.Empty,
interfaceAttributeData.Options,
Location.None)
Location.None,
exceptionToUnmanagedMarshallerQualifiedName)
{
IsExternallyDefined = true
},
Expand Down Expand Up @@ -285,6 +299,38 @@ private static bool OptionsAreValid(
return true;
}

private static bool ExceptionToUnmanagedMarshallerIsValid(
InterfaceDeclarationSyntax syntax,
GeneratedComInterfaceCompilationData attrSymbolInfo,
out string? exceptionToUnmanagedMarshallerFullyQualifiedName,
[NotNullWhen(false)] out DiagnosticInfo? exceptionToUnmanagedMarshallerDiagnostic)
{
GeneratedComInterfaceData attrInfo = GeneratedComInterfaceData.From(attrSymbolInfo);
if (attrSymbolInfo.ExceptionToUnmanagedMarshaller is INamedTypeSymbol exceptionToUnmanagedMarshallerType)
{
if (!exceptionToUnmanagedMarshallerType.IsAccessibleFromFileScopedClass(out var details))
{
exceptionToUnmanagedMarshallerDiagnostic = DiagnosticInfo.Create(
GeneratorDiagnostics.ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode,
syntax.Identifier.GetLocation(),
attrInfo.ExceptionToUnmanagedMarshaller.FullTypeName.Replace(TypeNames.GlobalAlias, ""),
details);
exceptionToUnmanagedMarshallerFullyQualifiedName = null;
return false;
}
else
{
exceptionToUnmanagedMarshallerFullyQualifiedName = exceptionToUnmanagedMarshallerType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
}
}
else
{
exceptionToUnmanagedMarshallerFullyQualifiedName = null;
}
exceptionToUnmanagedMarshallerDiagnostic = null;
return true;
}

/// <summary>
/// Returns true if there is 0 or 1 base Com interfaces (i.e. the inheritance is valid), and returns false when there are 2 or more base Com interfaces and sets <paramref name="diagnostic"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Microsoft.Interop
internal sealed record GeneratedComInterfaceData : InteropAttributeData
{
public ComInterfaceOptions Options { get; init; }
public ManagedTypeInfo? ExceptionToUnmanagedMarshaller { get; init; }
public static GeneratedComInterfaceData From(GeneratedComInterfaceCompilationData generatedComInterfaceAttr)
=> new GeneratedComInterfaceData() with
{
Expand All @@ -24,7 +25,10 @@ public static GeneratedComInterfaceData From(GeneratedComInterfaceCompilationDat
StringMarshallingCustomType = generatedComInterfaceAttr.StringMarshallingCustomType is not null
? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(generatedComInterfaceAttr.StringMarshallingCustomType)
: null,
Options = generatedComInterfaceAttr.Options
Options = generatedComInterfaceAttr.Options,
ExceptionToUnmanagedMarshaller = generatedComInterfaceAttr.ExceptionToUnmanagedMarshaller is not null
? ManagedTypeInfo.CreateTypeInfoForTypeSymbol(generatedComInterfaceAttr.ExceptionToUnmanagedMarshaller)
: null,
};
}

Expand All @@ -35,6 +39,7 @@ public static GeneratedComInterfaceData From(GeneratedComInterfaceCompilationDat
internal sealed record GeneratedComInterfaceCompilationData : InteropAttributeCompilationData
{
public ComInterfaceOptions Options { get; init; } = ComInterfaceOptions.ManagedObjectWrapper | ComInterfaceOptions.ComObjectWrapper;
public INamedTypeSymbol? ExceptionToUnmanagedMarshaller { get; init; }

public static bool TryGetGeneratedComInterfaceAttributeFromInterface(INamedTypeSymbol interfaceSymbol, [NotNullWhen(true)] out AttributeData? generatedComInterfaceAttribute)
{
Expand Down Expand Up @@ -70,6 +75,17 @@ public static GeneratedComInterfaceCompilationData GetDataFromAttribute(Attribut
Options = (ComInterfaceOptions)options.Value
};
}
if (args.TryGetValue(nameof(ExceptionToUnmanagedMarshaller), out TypedConstant exceptionToUnmanagedMarshaller))
{
if (exceptionToUnmanagedMarshaller.Value is not INamedTypeSymbol)
{
return null;
}
generatedComInterfaceAttributeData = generatedComInterfaceAttributeData with
{
ExceptionToUnmanagedMarshaller = (INamedTypeSymbol)exceptionToUnmanagedMarshaller.Value
};
}
return generatedComInterfaceAttributeData;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ public class Ids
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode =
DiagnosticDescriptorHelper.Create(
Ids.InvalidGeneratedComInterfaceAttributeUsage,
GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageTitle)),
GetResourceString(nameof(SR.ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true);

/// <inheritdoc cref="SR.InvalidExceptionMarshallingConfigurationMessage"/>
public static readonly DiagnosticDescriptor InvalidExceptionMarshallingConfiguration =
DiagnosticDescriptorHelper.Create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ private static MarshallingInfo CreateExceptionMarshallingInfo(AttributeData virt

if (virtualMethodIndexData.ExceptionMarshalling == ExceptionMarshalling.Com)
{
return new ComExceptionMarshalling();
return new ComExceptionMarshalling(null);
}
if (virtualMethodIndexData.ExceptionMarshalling == ExceptionMarshalling.Custom)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -916,4 +916,7 @@
<data name="BaseInterfaceDefinedInOtherAssemblyTitle" xml:space="preserve">
<value>Specifying 'GeneratedComInterfaceAttribute' on an interface that has a base interface defined in another assembly is not supported</value>
</data>
<data name="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode" xml:space="preserve">
<value>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Zařazovací typ vstupního bodu {0} pro typ {1} musí být typ s nejméně jedním atributem System.Runtime.InteropServices.CustomMarshallerAttribute, který tento typ určuje jako spravovaný typ.</target>
<note />
</trans-unit>
<trans-unit id="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode">
<source>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</source>
<target state="new">The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</target>
<note />
</trans-unit>
<trans-unit id="ExtraneousMarshallingInfo">
<source>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</source>
<target state="translated">Informace o zařazování se určily pro ElementIndirectionDepth {0}, ale informace o zařazování byly potřebné pouze pro {1} úrovně indirekce.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Der Marshallertyp "{0}" des Eintrittspunkts für den Typ "{1}" muss ein Typ mit mindestens einem "System.Runtime.InteropServices.CustomMarshallerAttribute" sein, der diesen Typ als verwalteten Typ angibt</target>
<note />
</trans-unit>
<trans-unit id="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode">
<source>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</source>
<target state="new">The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</target>
<note />
</trans-unit>
<trans-unit id="ExtraneousMarshallingInfo">
<source>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</source>
<target state="translated">Marshallinginformationen wurden für \"ElementIndirectionDepth\" {0}angegeben. Marshallinginformationen wurden jedoch nur für {1} Dereferenzierungsebene(n) benötigt.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">El tipo de serializador de punto de entrada "{0}" para el tipo "{1}" debe ser un tipo con al menos un "System.Runtime.InteropServices.CustomMarshallerAttribute" que especifique este tipo como el tipo administrado</target>
<note />
</trans-unit>
<trans-unit id="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode">
<source>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</source>
<target state="new">The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</target>
<note />
</trans-unit>
<trans-unit id="ExtraneousMarshallingInfo">
<source>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</source>
<target state="translated">Se especificó información de serialización para “ElementIndirectionDepth” {0}, pero la información de serialización solo era necesaria para {1} niveles de direccionamiento indirecto</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Le type de marshaleur de point d’entrée « {0} » pour le type « {1} » doit être un type avec au moins un type « System.Runtime.InteropServices.CustomMarspiaerAttribute » qui spécifie ce type comme type managé</target>
<note />
</trans-unit>
<trans-unit id="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode">
<source>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</source>
<target state="new">The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</target>
<note />
</trans-unit>
<trans-unit id="ExtraneousMarshallingInfo">
<source>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</source>
<target state="translated">Des informations de marshaling ont été spécifiées pour « ElementIndirectionDepth » {0}, mais les informations de marshaling étaient uniquement nécessaires pour {1} niveau(s) d’indirection</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Il tipo di marshaller del punto di ingresso '{0}' per il tipo '{1}' deve essere un tipo con almeno un elemento 'System.Runtime.InteropServices.CustomMarshallerAttribute' che specifica questo tipo come tipo gestito</target>
<note />
</trans-unit>
<trans-unit id="ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode">
<source>The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</source>
<target state="new">The type '{0}' specified as 'GeneratedComInterfaceAttribute.ExceptionToUnmanagedMarshaller' is not accessible by generated code. The type must have at least 'internal' accessibility. {1}</target>
<note />
</trans-unit>
<trans-unit id="ExtraneousMarshallingInfo">
<source>Marshalling info was specified for 'ElementIndirectionDepth' {0}, but marshalling info was only needed for {1} level(s) of indirection</source>
<target state="translated">Sono state specificate informazioni di marshalling per l'elemento 'ElementIndirectionDepth' {0}, ma le informazioni di marshalling sono necessarie solo per {1} livello/i di riferimento indiretto</target>
Expand Down
Loading
Loading