Skip to content

Commit a9f8ff6

Browse files
committed
Remove reserved parameters from friendly overloads
Closes #840
1 parent 78b4856 commit a9f8ff6

File tree

3 files changed

+20
-10
lines changed

3 files changed

+20
-10
lines changed

src/Microsoft.Windows.CsWin32/Generator.FriendlyOverloads.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
5555
MethodSignature<TypeHandleInfo> originalSignature = methodDefinition.DecodeSignature(SignatureHandleProvider.Instance, null);
5656
var parameters = externMethodDeclaration.ParameterList.Parameters.Select(StripAttributes).ToList();
5757
var lengthParamUsedBy = new Dictionary<int, int>();
58+
var parametersToRemove = new List<int>();
5859
var arguments = externMethodDeclaration.ParameterList.Parameters.Select(p => Argument(IdentifierName(p.Identifier.Text)).WithRefKindKeyword(p.Modifiers.FirstOrDefault(p => p.Kind() is SyntaxKind.RefKeyword or SyntaxKind.OutKeyword or SyntaxKind.InKeyword))).ToList();
5960
TypeSyntax? externMethodReturnType = externMethodDeclaration.ReturnType.WithoutLeadingTrivia();
6061
var fixedBlocks = new List<VariableDeclarationSyntax>();
@@ -72,6 +73,8 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
7273
}
7374

7475
bool isOptional = (param.Attributes & ParameterAttributes.Optional) == ParameterAttributes.Optional;
76+
bool isReserved = this.FindInteropDecorativeAttribute(param.GetCustomAttributes(), "ReservedAttribute") is not null;
77+
isOptional |= isReserved; // Per metadata decision made at https://github.com/microsoft/win32metadata/issues/1421#issuecomment-1372608090
7578
bool isIn = (param.Attributes & ParameterAttributes.In) == ParameterAttributes.In;
7679
bool isConst = this.FindInteropDecorativeAttribute(param.GetCustomAttributes(), "ConstAttribute") is not null;
7780
bool isComOutPtr = this.FindInteropDecorativeAttribute(param.GetCustomAttributes(), "ComOutPtrAttribute") is not null;
@@ -90,7 +93,14 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
9093
bool isManagedParameterType = this.IsManagedType(parameterTypeInfo);
9194
IdentifierNameSyntax origName = IdentifierName(externParam.Identifier.ValueText);
9295

93-
if (isManagedParameterType && (externParam.Modifiers.Any(SyntaxKind.OutKeyword) || externParam.Modifiers.Any(SyntaxKind.RefKeyword)))
96+
if (isReserved && !isOut)
97+
{
98+
// Remove the parameter and supply the default value for the type to the extern method.
99+
arguments[param.SequenceNumber - 1] = Argument(LiteralExpression(SyntaxKind.DefaultLiteralExpression));
100+
parametersToRemove.Add(param.SequenceNumber - 1);
101+
signatureChanged = true;
102+
}
103+
else if (isManagedParameterType && (externParam.Modifiers.Any(SyntaxKind.OutKeyword) || externParam.Modifiers.Any(SyntaxKind.RefKeyword)))
94104
{
95105
bool hasOut = externParam.Modifiers.Any(SyntaxKind.OutKeyword);
96106
arguments[param.SequenceNumber - 1] = arguments[param.SequenceNumber - 1].WithRefKindKeyword(TokenWithSpace(hasOut ? SyntaxKind.OutKeyword : SyntaxKind.RefKeyword));
@@ -487,15 +497,13 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
487497

488498
if (signatureChanged)
489499
{
490-
if (lengthParamUsedBy.Count > 0)
500+
// Remove in reverse order so as to not invalidate the indexes of elements to remove.
501+
// Also take care to only remove each element once, even if it shows up multiple times in the collection.
502+
SortedSet<int> parameterIndexesToRemove = new(lengthParamUsedBy.Keys);
503+
parameterIndexesToRemove.UnionWith(parametersToRemove);
504+
foreach (int indexToRemove in parameterIndexesToRemove.Reverse())
491505
{
492-
// Remove in reverse order so as to not invalidate the indexes of elements to remove.
493-
// Also take care to only remove each element once, even if it shows up multiple times in the collection.
494-
var parameterIndexesToRemove = new SortedSet<int>(lengthParamUsedBy.Keys);
495-
foreach (int indexToRemove in parameterIndexesToRemove.Reverse())
496-
{
497-
parameters.RemoveAt(indexToRemove);
498-
}
506+
parameters.RemoveAt(indexToRemove);
499507
}
500508

501509
TypeSyntax docRefExternName = overloadOf == FriendlyOverloadOf.InterfaceMethod

test/GenerationSandbox.Tests/GeneratedForm.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private static void PROC_InSignatureChangedToIntPtr()
4545

4646
private static void RegKeyHandle()
4747
{
48-
WIN32_ERROR status = PInvoke.RegLoadAppKey(string.Empty, out SafeRegistryHandle handle, 0, 0, 0);
48+
WIN32_ERROR status = PInvoke.RegLoadAppKey(string.Empty, out SafeRegistryHandle handle, 0, 0);
4949
}
5050

5151
private static void PreserveSigBasedOnMetadata()

test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ public void InterestingAPIs(
152152
"PZZWSTR",
153153
"PCZZSTR",
154154
"PCZZWSTR",
155+
"LoadLibraryEx", // method with a reserved parameter
156+
"IEnumNetCfgComponent", // interface with a method containing an `[Reserved] out` parameter (bonkers, I know).
155157
"IEventSubscription",
156158
"IRealTimeStylusSynchronization", // uses the `lock` C# keyword.
157159
"IHTMLInputElement", // has a field named `checked`, a C# keyword.

0 commit comments

Comments
 (0)