Skip to content

ComInterfaceGenerator produces invalid C# for partial interface methods. #94273

@jtschuster

Description

@jtschuster

Looks like we just copy the partial modifier from user code when we shouldn't

Code:

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(IID)]
internal partial interface ICalculator
{
    int Add(int a, int b);
    public partial int Subtract(int a, int b) => 0;
    public partial int Subtract(int a, int b);
    public const string IID = "c67121c6-cf26-431f-adc7-d12fe2448841";
}

generated:

// <auto-generated />
#pragma warning disable CS0612, CS0618
file unsafe class InterfaceInformation : global::System.Runtime.InteropServices.Marshalling.IIUnknownInterfaceType
{
    public static global::System.Guid Iid { get; } = new(new global::System.ReadOnlySpan<byte>(new byte[] { 198, 33, 113, 198, 38, 207, 31, 67, 173, 199, 209, 47, 226, 68, 136, 65 }));

    private static void** _vtable;
    public static void** ManagedVirtualMethodTable => _vtable != null ? _vtable : (_vtable = InterfaceImplementation.CreateManagedVirtualFunctionTable());
}

[global::System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute]
file unsafe partial interface InterfaceImplementation : global::Tutorial.ICalculator
{
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.ComInterfaceGenerator", "8.0.8.41904")]
    [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute]
    int global::Tutorial.ICalculator.Add(int a, int b)
    {
        var(__this, __vtable_native) = ((global::System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::Tutorial.ICalculator));
        int __retVal;
        int __invokeRetVal;
        {
            __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, int, int, int*, int> )__vtable_native[3])(__this, a, b, &__retVal);
        }

        // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
        global::System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);
        global::System.GC.KeepAlive(this);
        return __retVal;
    }

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.ComInterfaceGenerator", "8.0.8.41904")]
    [global::System.Runtime.CompilerServices.SkipLocalsInitAttribute]
    partial int global::Tutorial.ICalculator.Subtract(int a, int b)
    {
        var(__this, __vtable_native) = ((global::System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider)this).GetVirtualMethodTableInfoForKey(typeof(global::Tutorial.ICalculator));
        int __retVal;
        int __invokeRetVal;
        {
            __invokeRetVal = ((delegate* unmanaged[MemberFunction]<void*, int, int, int*, int> )__vtable_native[4])(__this, a, b, &__retVal);
        }

        // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
        global::System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(__invokeRetVal);
        global::System.GC.KeepAlive(this);
        return __retVal;
    }
}

file unsafe partial interface InterfaceImplementation
{
    [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(global::System.Runtime.CompilerServices.CallConvMemberFunction) })]
    internal static int ABI_Add(global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int a, int b, int* __invokeRetValUnmanaged__param)
    {
        global::Tutorial.ICalculator @this = default;
        ref int __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param;
        int __invokeRetVal = default;
        int __retVal = default;
        try
        {
            // Unmarshal - Convert native data to managed data.
            @this = global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::Tutorial.ICalculator>(__this_native);
            __invokeRetVal = @this.Add(a, b);
            // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
            __retVal = 0; // S_OK
            // Marshal - Convert managed data to native data.
            __invokeRetValUnmanaged = __invokeRetVal;
        }
        catch (global::System.Exception __exception)
        {
            __retVal = global::System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);
        }

        return __retVal;
    }

    [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(global::System.Runtime.CompilerServices.CallConvMemberFunction) })]
    internal static int ABI_Subtract(global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch* __this_native, int a, int b, int* __invokeRetValUnmanaged__param)
    {
        global::Tutorial.ICalculator @this = default;
        ref int __invokeRetValUnmanaged = ref *__invokeRetValUnmanaged__param;
        int __invokeRetVal = default;
        int __retVal = default;
        try
        {
            // Unmarshal - Convert native data to managed data.
            @this = global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch.GetInstance<global::Tutorial.ICalculator>(__this_native);
            __invokeRetVal = @this.Subtract(a, b);
            // NotifyForSuccessfulInvoke - Keep alive any managed objects that need to stay alive across the call.
            __retVal = 0; // S_OK
            // Marshal - Convert managed data to native data.
            __invokeRetValUnmanaged = __invokeRetVal;
        }
        catch (global::System.Exception __exception)
        {
            __retVal = global::System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<int>.ConvertToUnmanaged(__exception);
        }

        return __retVal;
    }
}

file unsafe partial interface InterfaceImplementation
{
    internal static void** CreateManagedVirtualFunctionTable()
    {
        void** vtable = (void**)global::System.Runtime.CompilerServices.RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(global::Tutorial.ICalculator), sizeof(void*) * 5);
        {
            nint v0, v1, v2;
            global::System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl(out v0, out v1, out v2);
            vtable[0] = (void*)v0;
            vtable[1] = (void*)v1;
            vtable[2] = (void*)v2;
        }

        {
            vtable[3] = (void*)(delegate* unmanaged[MemberFunction]<global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch*, int, int, int*, int> )&ABI_Add;
            vtable[4] = (void*)(delegate* unmanaged[MemberFunction]<global::System.Runtime.InteropServices.ComWrappers.ComInterfaceDispatch*, int, int, int*, int> )&ABI_Subtract;
        }

        return vtable;
    }
}

namespace Tutorial
{
    [global::System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute<InterfaceInformation, InterfaceImplementation>]
    internal partial interface ICalculator
    {
    }
}

namespace Tutorial
{
    internal partial interface ICalculator
    {
    }
}

Error:

Microsoft.Interop.ComInterfaceGenerator\ICalculator.cs(33,46): error CS0759: No defining declaration found for implementing declaration of partial method 'InterfaceImplementation.Subtract(int, int)'

Microsoft.Interop.ComInterfaceGenerator\ICalculator.cs(33,46): error CS0754: A partial method may not explicitly implement an interface method

Microsoft.Interop.ComInterfaceGenerator\ICalculator.cs(33,46): error CS0539: 'InterfaceImplementation.Subtract(int, int)' in explicit interface declaration is not found among members of the interface that can be implemented

Microsoft.Interop.ComInterfaceGenerator\ICalculator.cs(33,46): error CS8796: Partial method 'InterfaceImplementation.Subtract(int, int)' must have accessibility modifiers because it has a non-void return type. 

Metadata

Metadata

Labels

area-System.Runtime.InteropServicesin-prThere is an active PR which will close this issue when it is mergedsource-generatorIndicates an issue with a source generator feature

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions