Skip to content

Commit 5d173d9

Browse files
committed
Collapse methods with same name into same COM slot
The IOpenFileDialog declared in the .NET 7 in such way that methods from IFileDialog duplicated which lead to incorrect counting of COM slot location
1 parent 51570e3 commit 5d173d9

File tree

2 files changed

+159
-3
lines changed

2 files changed

+159
-3
lines changed

WinFormsComInterop.SourceGenerator.Tests/RuntimeCallableWrapperTest.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,6 +2427,138 @@ unsafe partial class C : global::Foo.IStr2
24272427
}
24282428
}
24292429
}
2430+
}";
2431+
Assert.AreEqual(expectedOutput, output);
2432+
}
2433+
2434+
[TestMethod]
2435+
public void InheritanceSameMethods()
2436+
{
2437+
string source = @"
2438+
namespace Foo
2439+
{
2440+
using System.Runtime.InteropServices;
2441+
2442+
[Guid(""22DD68D1-86FD-4332-8666-9ABEDEA2D24C"")]
2443+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
2444+
public interface IStr
2445+
{
2446+
void Read(byte* pv, uint cb, uint* pcbRead);
2447+
string Name { get; }
2448+
}
2449+
2450+
[Guid(""22DD68D2-86FD-4332-8666-9ABEDEA2D24C"")]
2451+
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
2452+
public interface IStr2: IStr
2453+
{
2454+
void Read(byte* pv, uint cb, uint* pcbRead);
2455+
string Name { get; }
2456+
void Write(byte* pv, uint cb, uint* pcbRead);
2457+
}
2458+
2459+
[RuntimeCallableWrapper(typeof(IStr2))]
2460+
partial class C
2461+
{
2462+
}
2463+
}";
2464+
string output = this.GetGeneratedOutput(source, NullableContextOptions.Disable);
2465+
2466+
Assert.IsNotNull(output);
2467+
2468+
var expectedOutput = @"// <auto-generated>
2469+
// Code generated by COM Proxy Code Generator.
2470+
// Changes may cause incorrect behavior and will be lost if the code is
2471+
// regenerated.
2472+
// </auto-generated>
2473+
#nullable enable
2474+
using Marshal = System.Runtime.InteropServices.Marshal;
2475+
2476+
namespace Foo
2477+
{
2478+
[System.Runtime.Versioning.SupportedOSPlatform(""windows"")]
2479+
unsafe partial class C : global::Foo.IStr2
2480+
{
2481+
void global::Foo.IStr2.Read(byte* pv, uint cb, uint* pcbRead)
2482+
{
2483+
var targetInterface = new System.Guid(""22DD68D2-86FD-4332-8666-9ABEDEA2D24C"");
2484+
var result = Marshal.QueryInterface(this.instance, ref targetInterface, out var thisPtr);
2485+
if (result != 0)
2486+
{
2487+
throw new System.InvalidCastException();
2488+
}
2489+
2490+
try
2491+
{
2492+
var comDispatch = (System.IntPtr*)thisPtr;
2493+
var vtbl = (System.IntPtr*)comDispatch[0];
2494+
result = ((delegate* unmanaged<System.IntPtr, byte*, uint, uint*, int>)vtbl[3])(thisPtr, pv, cb, pcbRead);
2495+
if (result != 0)
2496+
{
2497+
Marshal.ThrowExceptionForHR(result);
2498+
}
2499+
2500+
}
2501+
finally
2502+
{
2503+
Marshal.Release(thisPtr);
2504+
}
2505+
}
2506+
string global::Foo.IStr2.Name
2507+
{
2508+
get
2509+
{
2510+
var targetInterface = new System.Guid(""22DD68D2-86FD-4332-8666-9ABEDEA2D24C"");
2511+
var result = Marshal.QueryInterface(this.instance, ref targetInterface, out var thisPtr);
2512+
if (result != 0)
2513+
{
2514+
throw new System.InvalidCastException();
2515+
}
2516+
2517+
try
2518+
{
2519+
var comDispatch = (System.IntPtr*)thisPtr;
2520+
var vtbl = (System.IntPtr*)comDispatch[0];
2521+
System.IntPtr retVal;
2522+
result = ((delegate* unmanaged<System.IntPtr, System.IntPtr*, int>)vtbl[4])(thisPtr, &retVal);
2523+
if (result != 0)
2524+
{
2525+
Marshal.ThrowExceptionForHR(result);
2526+
}
2527+
2528+
return retVal == System.IntPtr.Zero ? null : (string)Marshal.PtrToStringUni(retVal);
2529+
}
2530+
finally
2531+
{
2532+
Marshal.Release(thisPtr);
2533+
}
2534+
}
2535+
}
2536+
void global::Foo.IStr2.Write(byte* pv, uint cb, uint* pcbRead)
2537+
{
2538+
var targetInterface = new System.Guid(""22DD68D2-86FD-4332-8666-9ABEDEA2D24C"");
2539+
var result = Marshal.QueryInterface(this.instance, ref targetInterface, out var thisPtr);
2540+
if (result != 0)
2541+
{
2542+
throw new System.InvalidCastException();
2543+
}
2544+
2545+
try
2546+
{
2547+
var comDispatch = (System.IntPtr*)thisPtr;
2548+
var vtbl = (System.IntPtr*)comDispatch[0];
2549+
result = ((delegate* unmanaged<System.IntPtr, byte*, uint, uint*, int>)vtbl[5])(thisPtr, pv, cb, pcbRead);
2550+
if (result != 0)
2551+
{
2552+
Marshal.ThrowExceptionForHR(result);
2553+
}
2554+
2555+
}
2556+
finally
2557+
{
2558+
Marshal.Release(thisPtr);
2559+
}
2560+
}
2561+
}
24302562
}";
24312563
Assert.AreEqual(expectedOutput, output);
24322564
}

WinFormsComInterop.SourceGenerator/WrapperGenerationContext.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,26 @@ public MethodGenerationContext CreateMethodGenerationContext(
9393
return existingContext;
9494
}
9595

96+
var originalMethod = method;
97+
IMethodSymbol? previousMethod = null;
98+
MethodGenerationContext? previousMethodContext = null;
99+
if (method.ContainingType.AllInterfaces.Length != 0)
100+
{
101+
foreach (var parentInterface in method.ContainingType.AllInterfaces)
102+
{
103+
var sameMembers = parentInterface.GetMembers(method.Name);
104+
105+
// I do not distinguish between methods with same name and different parameters.
106+
var previousMember = sameMembers.SingleOrDefault();
107+
if (previousMember != null)
108+
{
109+
previousMethod = (IMethodSymbol)previousMember;
110+
previousMethodContext = CreateMethodGenerationContext(classSymbol, previousMethod, ref comSlotNumber);
111+
break;
112+
}
113+
}
114+
}
115+
96116
var preserveSigAttribute = method.GetAttributes().FirstOrDefault(ad =>
97117
{
98118
var attributeName = ad.AttributeClass?.ToDisplayString();
@@ -101,13 +121,17 @@ public MethodGenerationContext CreateMethodGenerationContext(
101121
});
102122
var preserveSignature = preserveSigAttribute != null;
103123
preserveSignature |= (method.MethodImplementationFlags & System.Reflection.MethodImplAttributes.PreserveSig) == System.Reflection.MethodImplAttributes.PreserveSig;
104-
var methodContext = new MethodGenerationContext(method, classSymbol.Type, this)
124+
var methodContext = new MethodGenerationContext(originalMethod, classSymbol.Type, this)
105125
{
106126
PreserveSignature = preserveSignature,
107-
ComSlotNumber = comSlotNumber,
127+
ComSlotNumber = previousMethodContext?.ComSlotNumber ?? comSlotNumber,
108128
};
109129
contextCache.Add(key, methodContext);
110-
comSlotNumber++;
130+
if (previousMethodContext is null)
131+
{
132+
comSlotNumber++;
133+
}
134+
111135
return methodContext;
112136
}
113137

0 commit comments

Comments
 (0)