Skip to content
Merged
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
6 changes: 6 additions & 0 deletions src/CsWin32Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ private Task<bool> GenerateCode(
{
superGenerator.AddGeneratorExclusion(trimmedLine[1..]);
}
else if (trimmedLine.Equals("IDispatch", StringComparison.Ordinal))
{
// If IDispatch is explicitly requested then generate the full interface. By default
// only a placeholder IDispatch is generated, this overrides it.
superGenerator.GenerateIDispatch();
}
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.Windows.CsWin32/Generator.Com.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ public partial class Generator

private readonly HashSet<string> injectedPInvokeHelperMethodsToFriendlyOverloadsExtensions = new();

// If IDispatch is explicitly requested then we will generate the full IDispatch interface. SuperGenerator calls this when
// CsWin32Generator sees IDispatch in the NativeMethods.txt.
internal bool GenerateFullIDispatch { get; set; }

// With runtime marshaling, IDispatch is implicitly generated when using InterfaceIsDual. The COM source generators
// don't support this, so we generate a dummy IDispatch when using source generators mode. We don't generate a "real"
// IDispatch because the interface would be expensive and not very useful. We just need to have placeholder vtable slots.
Expand Down Expand Up @@ -707,7 +711,10 @@ static ExpressionSyntax ThisPointer(PointerTypeSyntax? typedPointer = null)

if (this.Reader.StringComparer.Equals(typeDef.Name, "IDispatch"))
{
return this.GenerateIDispatch ? this.CreateIDispatchTypeDeclarationSyntax() : null;
if (!this.GenerateFullIDispatch)
{
return this.GenerateIDispatch ? this.CreateIDispatchTypeDeclarationSyntax() : null;
}
}

string actualIfaceName = this.Reader.GetString(typeDef.Name);
Expand Down
12 changes: 12 additions & 0 deletions src/Microsoft.Windows.CsWin32/SuperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ public void AddGeneratorExclusion(string exclusion)
}
}

/// <summary>
/// Tells the generator to generate the "real" IDispatch interface fully. This only really has an
/// effect in COM source generator mode.
/// </summary>
public void GenerateIDispatch()
{
foreach (Generator generator in this.Generators.Values)
{
generator.GenerateFullIDispatch = true;
}
}

/// <summary>
/// Looks up the <see cref="Generator"/> that owns a referenced type.
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions test/CsWin32Generator.Tests/CsWin32GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public async Task TestGenerateIDispatch()

var idispatchType = this.FindGeneratedType("IDispatch");
Assert.NotEmpty(idispatchType);

// And when generating IDispatch explicitly it should have "real" methods on it.
var methods = idispatchType.SelectMany(t => t.DescendantNodes().OfType<MethodDeclarationSyntax>());
var method = Assert.Single(methods, m => m.Identifier.Text == "GetTypeInfoCount");
Assert.Contains("(out uint pctinfo)", method.ParameterList.ToString());
}

[Fact]
Expand All @@ -39,6 +44,13 @@ public async Task TestGenerateIShellWindows()

// Check that IShellWindows has IDispatch as a base
Assert.Contains(ishellWindowsType, x => x.BaseList?.Types.Any(t => t.Type.ToString().Contains("IDispatch")) ?? false);

var idispatchType = this.FindGeneratedType("IDispatch");
Assert.NotEmpty(idispatchType);

// And when generating IDispatch implicitly it should not have "real" methods on it.
var methods = idispatchType.SelectMany(t => t.DescendantNodes().OfType<MethodDeclarationSyntax>());
Assert.DoesNotContain(methods, m => m.Identifier.Text == "GetTypeInfoCount");
}

[Fact]
Expand Down
Loading