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

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -389,14 +389,30 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M

var virtualMethodIndexData = new VirtualMethodIndexData(index, ImplicitThisParameter: true, direction, true, ExceptionMarshalling.Com);

MarshallingInfo exceptionMarshallingInfo;

if (generatedComInterfaceAttributeData.ExceptionToUnmanagedMarshaller is not null)
{
exceptionMarshallingInfo = CustomMarshallingInfoHelper.CreateNativeMarshallingInfoForNonSignatureElement(
environment.Compilation.GetTypeByMetadataName(TypeNames.System_Exception),
generatedComInterfaceAttributeData.ExceptionToUnmanagedMarshaller,
generatedComAttribute,
environment.Compilation,
generatorDiagnostics);
}
else
{
exceptionMarshallingInfo = new ComExceptionMarshalling();
}

return new IncrementalMethodStubGenerationContext(
signatureContext,
containingSyntaxContext,
methodSyntaxTemplate,
locations,
callConv.ToSequenceEqualImmutableArray(SyntaxEquivalentComparer.Instance),
virtualMethodIndexData,
new ComExceptionMarshalling(),
exceptionMarshallingInfo,
environment.EnvironmentFlags,
owningInterface,
declaringType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,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 DiagnosticInfo? exceptionToUnmanagedMarshallerDiagnostic))
return DiagnosticOrInterfaceInfo.From(exceptionToUnmanagedMarshallerDiagnostic);

InterfaceInfo info = (
new ComInterfaceInfo(
ManagedTypeInfo.CreateTypeInfoForTypeSymbol(symbol),
Expand Down Expand Up @@ -285,6 +288,27 @@ private static bool OptionsAreValid(
return true;
}

private static bool ExceptionToUnmanagedMarshallerIsValid(
InterfaceDeclarationSyntax syntax,
GeneratedComInterfaceCompilationData attrSymbolInfo,
[NotNullWhen(false)] out DiagnosticInfo? exceptionToUnmanagedMarshallerDiagnostic)
{
if (attrSymbolInfo.ExceptionToUnmanagedMarshaller is INamedTypeSymbol exceptionToUnmanagedMarshallerType)
{
if (!exceptionToUnmanagedMarshallerType.IsAccessibleFromFileScopedClass(out var details))
{
exceptionToUnmanagedMarshallerDiagnostic = DiagnosticInfo.Create(
GeneratorDiagnostics.ExceptionToUnmanagedMarshallerNotAccessibleByGeneratedCode,
syntax.Identifier.GetLocation(),
exceptionToUnmanagedMarshallerType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat).Replace(TypeNames.GlobalAlias, ""),
details);
return false;
}
}
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 @@ -35,6 +35,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 +71,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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally don't use null for logical flow. This would mean the return type should be updated. I don't think we should be failing in this case as it will silent and difficult to reason about. A diagnostic would be more appropriate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was based on InteropAttributeDataExtensions.WithValuesFromNamedArguments, which returns null if an invalid StringMarshalling type is passed. I'm not sure how this is handled particularly by GeneratedComInterfaceAttributeData.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is another one of the cases where we use null as our "unable to parse" sentinel (and in the caller we check for null and emit a diagnostic and replace with a sentinel). Can you confirm?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it looks like there's no regards to null-checking here. Even today if you pass in an invalid type for StringMarshallingCustomType, i.e.

[GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(string*))]
public partial interface Foo;

the generator fails with a generic error message from Roslyn:

Generator 'ComInterfaceGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'NullReferenceException' with message 'Object reference not set to an instance of an object.'

}
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 @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">型 '{1}' 向けのエントリ ポイント マーシャラー型 '{0}' は、この型をマネージド型として指定する 'System.Runtime.InteropServices.CustomMarshallerAttribute' を 1 つ以上持つ型である必要があります</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">'ElementIndirectionDepth' {0} にマーシャリング情報が指定されましたが、マーシャリング情報は間接参照の {1} レベルにのみ必要です</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">'{1}' 유형의 진입점 마샬러 유형 '{0}'은(는) 이 유형을 관리 유형으로 지정하는 'System.Runtime.InteropServices.CustomMarshallerAttribute'가 하나 이상 있는 유형이어야 합니다.</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">마샬링 정보가 'ElementIndirectionDepth' {0}에 대해 지정되었지만 마샬링 정보는 간접 참조 수준 {1}에만 필요했습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Typ marshallera punktu wejścia „{0}” dla typu „{1}” musi być typem z co najmniej jednym atrybutem „System.Runtime.InteropServices.CustomMarshaellerAttribute”, który określa ten typ jako typ zarządzany</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">Informacje dotyczące skierowania zostały określone dla elementu „ElementIndirectionDepth” {0}, ale informacje dotyczące skierowania były potrzebne tylko dla poziomów pośrednich w liczbie {1}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">O tipo de empacotador de ponto de entrada '{0}' para o tipo '{1}' deve ser um tipo com pelo menos um 'System.Runtime.InteropServices.CustomMarshallerAttribute' que especifica esse tipo como o tipo gerenciado</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">As informações de marshalling foram especificadas para o {0} 'ElementIndirectionDepth', mas as informações de marshalling só eram necessárias para {1} nível(s) de indireção</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@
<target state="translated">Тип маршалера точки входа "{0}" для типа "{1}" должен содержать хотя бы один атрибут "System.Runtime.InteropServices.CustomMarshallerAttribute", указывающий этот тип в качестве управляемого типа</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">Для \"ElementIndirectionDepth\" {0} указаны сведения маршализации, но они необходимы только для {1} уровней косвенного обращения</target>
Expand Down
Loading
Loading