From 343805f4017311eb65edf0eb465cb47e51afb757 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 16 Mar 2023 14:20:57 -0600 Subject: [PATCH 1/5] Bump metadata to 46.0.5-preview --- Directory.Packages.props | 2 +- test/GenerationSandbox.Tests/BasicTests.cs | 2 +- test/GenerationSandbox.Tests/ComRuntimeTests.cs | 4 ++-- test/GenerationSandbox.Tests/NativeMethods.txt | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 61b105a9..4fc3ea82 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ true true - 41.0.25-preview + 46.0.5-preview 0.1.12-alpha diff --git a/test/GenerationSandbox.Tests/BasicTests.cs b/test/GenerationSandbox.Tests/BasicTests.cs index b4c62419..1012c7e8 100644 --- a/test/GenerationSandbox.Tests/BasicTests.cs +++ b/test/GenerationSandbox.Tests/BasicTests.cs @@ -200,7 +200,7 @@ public void CreateFile() var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); using var fileHandle = PInvoke.CreateFile( path, - FILE_ACCESS_FLAGS.FILE_GENERIC_WRITE, + (uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE, FILE_SHARE_MODE.FILE_SHARE_NONE, lpSecurityAttributes: default, FILE_CREATION_DISPOSITION.CREATE_NEW, diff --git a/test/GenerationSandbox.Tests/ComRuntimeTests.cs b/test/GenerationSandbox.Tests/ComRuntimeTests.cs index f05c2537..6a058844 100644 --- a/test/GenerationSandbox.Tests/ComRuntimeTests.cs +++ b/test/GenerationSandbox.Tests/ComRuntimeTests.cs @@ -14,8 +14,8 @@ public void RemotableInterface() IServiceProvider serviceProvider = (IServiceProvider)shellWindows.FindWindowSW( PInvoke.CSIDL_DESKTOP, null, - (int)ShellWindowTypeConstants.SWC_DESKTOP, + ShellWindowTypeConstants.SWC_DESKTOP, out int hwnd, - (int)ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH); + ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH); } } diff --git a/test/GenerationSandbox.Tests/NativeMethods.txt b/test/GenerationSandbox.Tests/NativeMethods.txt index 16543a6d..a9df0184 100644 --- a/test/GenerationSandbox.Tests/NativeMethods.txt +++ b/test/GenerationSandbox.Tests/NativeMethods.txt @@ -6,6 +6,7 @@ CreateFile CSIDL_DESKTOP DISPLAYCONFIG_VIDEO_SIGNAL_INFO EnumWindows +FILE_ACCESS_RIGHTS GetProcAddress GetTickCount GetWindowText From 532ed3a2cef46ce7818d136bcd730831e0950221 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 16 Mar 2023 15:47:52 -0600 Subject: [PATCH 2/5] Fix HGLOBAL and HLOCAL SafeHandles --- src/Microsoft.Windows.CsWin32/Generator.Handle.cs | 4 ++++ test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/Microsoft.Windows.CsWin32/Generator.Handle.cs b/src/Microsoft.Windows.CsWin32/Generator.Handle.cs index 512d66f0..27023cac 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.Handle.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.Handle.cs @@ -197,6 +197,10 @@ public partial class Generator ExpressionSyntax noerror = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ParseName("winmdroot.Foundation.WIN32_ERROR"), IdentifierName("NO_ERROR")); releaseInvocation = BinaryExpression(SyntaxKind.EqualsExpression, releaseInvocation, noerror); break; + case "HGLOBAL": + case "HLOCAL": + releaseInvocation = BinaryExpression(SyntaxKind.EqualsExpression, releaseInvocation, DefaultExpression(releaseMethodReturnType.Type)); + break; default: throw new NotSupportedException($"Return type {identifierName.Identifier.ValueText} on release method {releaseMethod} not supported."); } diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs index 1b39e6c1..36db07be 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs @@ -152,6 +152,7 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "PZZWSTR", "PCZZSTR", "PCZZWSTR", + "LocalLock", // returns HLOCAL, which requires special release support "LoadLibraryEx", // method with a reserved parameter "IEnumNetCfgComponent", // interface with a method containing an `[Reserved] out` parameter (bonkers, I know). "IEventSubscription", From 470ba4cfc6d588bc7a14edd54af0f62226092811 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 16 Mar 2023 18:15:49 -0600 Subject: [PATCH 3/5] CreateFile change mitigations --- test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs index 36db07be..db195bc1 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs @@ -894,6 +894,7 @@ public void ProjectReferenceBetweenThreeGeneratingProjects(bool internalsVisible // Now produce more code in a referencing project that needs HANDLE, which is found *twice*, once in each referenced project. this.generator = this.CreateGenerator(); Assert.True(this.generator.TryGenerate("CreateFile", CancellationToken.None)); + Assert.True(this.generator.TryGenerate("FILE_ACCESS_RIGHTS", CancellationToken.None)); this.CollectGeneratedCode(this.generator); // Consume the API to verify the user experience isn't broken. @@ -910,7 +911,7 @@ static unsafe void Main() { HANDLE h = PInvoke.CreateFile( default(PCWSTR), - FILE_ACCESS_FLAGS.FILE_ADD_FILE, + (uint)FILE_ACCESS_RIGHTS.FILE_ADD_FILE, FILE_SHARE_MODE.FILE_SHARE_READ, null, FILE_CREATION_DISPOSITION.CREATE_NEW, From 357a566cdd451fbebea8e05bc3dac75d6de7dba3 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 16 Mar 2023 20:05:42 -0600 Subject: [PATCH 4/5] Fix constant generation --- .../Generator.Constant.cs | 201 ++++++++++++++++-- .../SimpleSyntaxFactory.cs | 25 ++- .../ConstantsTests.cs | 31 +++ .../GeneratorTests.cs | 9 - 4 files changed, 234 insertions(+), 32 deletions(-) create mode 100644 test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs diff --git a/src/Microsoft.Windows.CsWin32/Generator.Constant.cs b/src/Microsoft.Windows.CsWin32/Generator.Constant.cs index 18baa372..081c90c2 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.Constant.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.Constant.cs @@ -158,28 +158,187 @@ internal void RequestConstant(FieldDefinitionHandle fieldDefHandle) }); } - private static ObjectCreationExpressionSyntax PropertyKeyValue(CustomAttribute propertyKeyAttribute, TypeSyntax type) + private static List> SplitConstantArguments(ReadOnlyMemory args) { - CustomAttributeValue args = propertyKeyAttribute.DecodeValue(CustomAttributeTypeProvider.Instance); - uint a = (uint)args.FixedArguments[0].Value!; - ushort b = (ushort)args.FixedArguments[1].Value!; - ushort c = (ushort)args.FixedArguments[2].Value!; - byte d = (byte)args.FixedArguments[3].Value!; - byte e = (byte)args.FixedArguments[4].Value!; - byte f = (byte)args.FixedArguments[5].Value!; - byte g = (byte)args.FixedArguments[6].Value!; - byte h = (byte)args.FixedArguments[7].Value!; - byte i = (byte)args.FixedArguments[8].Value!; - byte j = (byte)args.FixedArguments[9].Value!; - byte k = (byte)args.FixedArguments[10].Value!; - uint pid = (uint)args.FixedArguments[11].Value!; - - return ObjectCreationExpression(type).WithInitializer( - InitializerExpression(SyntaxKind.ObjectInitializerExpression, SeparatedList(new[] + List> argExpressions = new(); + + // Recursively parse the arguments, splitting on commas that are not nested within curly brances. + int start = 0; + int depth = 0; + for (int i = 0; i < args.Length; i++) + { + switch (args.Span[i]) + { + case '{': + depth++; + break; + case '}': + depth--; + break; + case ',': + if (depth == 0) + { + ReadOnlyMemory arg = args.Slice(start, i - start); + + argExpressions.Add(TrimCurlyBraces(arg)); + start = i + 1; + + // Trim a leading space if present. + if (args.Span[start] == ' ') + { + start++; + i++; + } + } + + break; + } + } + + if (start < args.Length) + { + argExpressions.Add(TrimCurlyBraces(args.Slice(start))); + } + + ReadOnlyMemory TrimCurlyBraces(ReadOnlyMemory arg) + { + return arg.Span[0] == '{' && arg.Span[arg.Length - 1] == '}' ? arg.Slice(1, arg.Length - 2) : arg; + } + + return argExpressions; + } + + private ObjectCreationExpressionSyntax? CreateConstantViaCtor(List> args, TypeSyntax targetType, TypeDefinition targetTypeDef) + { + foreach (MethodDefinitionHandle methodDefHandle in targetTypeDef.GetMethods()) + { + MethodDefinition methodDef = this.Reader.GetMethodDefinition(methodDefHandle); + if (this.Reader.StringComparer.Equals(methodDef.Name, ".ctor") && methodDef.GetParameters().Count == args.Count) + { + MethodSignature ctorSignature = methodDef.DecodeSignature(SignatureHandleProvider.Instance, null); + var argExpressions = new ArgumentSyntax[args.Count]; + + for (int i = 0; i < args.Count; i++) + { + TypeHandleInfo parameterTypeInfo = ctorSignature.ParameterTypes[i]; + argExpressions[i] = Argument(this.CreateConstant(args[i], parameterTypeInfo)); + i++; + } + + return ObjectCreationExpression(targetType).AddArgumentListArguments(argExpressions); + } + } + + return null; + } + + private ObjectCreationExpressionSyntax? CreateConstantByField(List> args, TypeSyntax targetType, TypeDefinition targetTypeDef) + { + if (targetTypeDef.GetFields().Count != args.Count) + { + return null; + } + + var fieldAssignmentExpressions = new AssignmentExpressionSyntax[args.Count]; + int i = 0; + foreach (FieldDefinitionHandle fieldDefHandle in targetTypeDef.GetFields()) + { + FieldDefinition fieldDef = this.Reader.GetFieldDefinition(fieldDefHandle); + string fieldName = this.Reader.GetString(fieldDef.Name); + TypeHandleInfo fieldTypeInfo = fieldDef.DecodeSignature(SignatureHandleProvider.Instance, null) with { IsConstantField = true }; + fieldAssignmentExpressions[i] = AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(fieldName), + this.CreateConstant(args[i], fieldTypeInfo)); + i++; + } + + return ObjectCreationExpression(targetType) + .WithArgumentList(null) + .WithInitializer(InitializerExpression(SyntaxKind.ObjectInitializerExpression, SeparatedList()).AddExpressions(fieldAssignmentExpressions)); + } + + private ExpressionSyntax CreateConstant(ReadOnlyMemory argsAsString, TypeHandleInfo targetType) + { + return targetType switch + { + ArrayTypeHandleInfo { ElementType: PrimitiveTypeHandleInfo { PrimitiveTypeCode: PrimitiveTypeCode.Byte } } pointerType => this.CreateByteArrayConstant(argsAsString), + PrimitiveTypeHandleInfo primitiveType => ToExpressionSyntax(primitiveType.PrimitiveTypeCode, argsAsString), + HandleTypeHandleInfo handleType => this.CreateConstant(argsAsString, targetType.ToTypeSyntax(this.fieldTypeSettings, null).Type, (TypeReferenceHandle)handleType.Handle), + _ => throw new GenerationFailedException($"Unsupported constant type: {targetType}"), + }; + } + + private ExpressionSyntax CreateConstant(ReadOnlyMemory argsAsString, TypeSyntax targetType, TypeReferenceHandle targetTypeRefHandle) + { + if (!this.TryGetTypeDefHandle(targetTypeRefHandle, out TypeDefinitionHandle targetTypeDefHandle)) + { + // Special case for System.Guid. + TypeReference typeRef = this.Reader.GetTypeReference(targetTypeRefHandle); + if (this.Reader.StringComparer.Equals(typeRef.Name, "Guid")) { - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("fmtid"), GuidValue(propertyKeyAttribute)), - AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("pid"), LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(pid))), - }))); + List> guidArgs = SplitConstantArguments(argsAsString); + return this.CreateGuidConstant(guidArgs); + } + + throw new GenerationFailedException("Unrecognized target type."); + } + + this.RequestInteropType(targetTypeDefHandle, this.DefaultContext); + TypeDefinition typeDef = this.Reader.GetTypeDefinition(targetTypeDefHandle); + + List> args = SplitConstantArguments(argsAsString); + + ObjectCreationExpressionSyntax? result = + this.CreateConstantViaCtor(args, targetType, typeDef) ?? + this.CreateConstantByField(args, targetType, typeDef); + + return result ?? throw new GenerationFailedException($"Unable to construct constant value given {args.Count} fields or constructor arguments."); + } + + private ExpressionSyntax CreateByteArrayConstant(ReadOnlyMemory argsAsString) + { + List> args = SplitConstantArguments(argsAsString); + TypeSyntax byteTypeSyntax = PredefinedType(Token(SyntaxKind.ByteKeyword)); + return CastExpression( + MakeReadOnlySpanOfT(byteTypeSyntax), + ArrayCreationExpression(ArrayType(byteTypeSyntax).AddRankSpecifiers(ArrayRankSpecifier())).WithInitializer(InitializerExpression(SyntaxKind.ArrayInitializerExpression, SeparatedList()) + .AddExpressions(args.Select(b => ToExpressionSyntax(PrimitiveTypeCode.Byte, b)).ToArray()))); + } + + private ExpressionSyntax CreateGuidConstant(List> guidArgs) + { + if (guidArgs.Count != 11) + { + throw new GenerationFailedException($"Unexpected element count {guidArgs.Count} when constructing a Guid, which requires 11."); + } + + var ctorArgs = new SyntaxToken[11] + { + Literal(uint.Parse(guidArgs[0].ToString(), CultureInfo.InvariantCulture)), + Literal(ushort.Parse(guidArgs[1].ToString(), CultureInfo.InvariantCulture)), + Literal(ushort.Parse(guidArgs[2].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[3].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[4].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[5].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[6].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[7].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[8].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[9].ToString(), CultureInfo.InvariantCulture)), + Literal(byte.Parse(guidArgs[10].ToString(), CultureInfo.InvariantCulture)), + }; + + return ObjectCreationExpression(GuidTypeSyntax).AddArgumentListArguments(ctorArgs.Select(t => Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, t))).ToArray()); + } + + private ExpressionSyntax CreateConstant(CustomAttribute constantAttribute, TypeHandleInfo targetType) + { + TypeReferenceHandle targetTypeRefHandle = (TypeReferenceHandle)((HandleTypeHandleInfo)targetType).Handle; + CustomAttributeValue args = constantAttribute.DecodeValue(CustomAttributeTypeProvider.Instance); + return this.CreateConstant( + ((string)args.FixedArguments[0].Value!).AsMemory(), + targetType.ToTypeSyntax(this.fieldTypeSettings, null).Type, + targetTypeRefHandle); } private FieldDeclarationSyntax DeclareConstant(FieldDefinition fieldDef) @@ -193,7 +352,7 @@ private FieldDeclarationSyntax DeclareConstant(FieldDefinition fieldDef) ExpressionSyntax value = fieldDef.GetDefaultValue() is { IsNil: false } constantHandle ? ToExpressionSyntax(this.Reader, constantHandle) : this.FindInteropDecorativeAttribute(customAttributes, nameof(GuidAttribute)) is CustomAttribute guidAttribute ? GuidValue(guidAttribute) : - this.FindInteropDecorativeAttribute(customAttributes, "PropertyKeyAttribute") is CustomAttribute propertyKeyAttribute ? PropertyKeyValue(propertyKeyAttribute, fieldType.Type) : + this.FindInteropDecorativeAttribute(customAttributes, "ConstantAttribute") is CustomAttribute constantAttribute ? this.CreateConstant(constantAttribute, fieldTypeInfo) : throw new NotSupportedException("Unsupported constant: " + name); bool requiresUnsafe = false; if (fieldType.Type is not PredefinedTypeSyntax && value is not ObjectCreationExpressionSyntax) diff --git a/src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs b/src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs index 418e5c8f..f19ad681 100644 --- a/src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs +++ b/src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs @@ -98,6 +98,7 @@ internal static class SimpleSyntaxFactory internal static readonly SyntaxToken SemicolonWithLineFeed = TokenWithLineFeed(SyntaxKind.SemicolonToken); internal static readonly IdentifierNameSyntax InlineArrayIndexerExtensionsClassName = IdentifierName("InlineArrayIndexerExtensions"); internal static readonly TypeSyntax SafeHandleTypeSyntax = IdentifierName("SafeHandle"); + internal static readonly IdentifierNameSyntax GuidTypeSyntax = IdentifierName(nameof(Guid)); internal static readonly IdentifierNameSyntax IntPtrTypeSyntax = IdentifierName(nameof(IntPtr)); internal static readonly IdentifierNameSyntax UIntPtrTypeSyntax = IdentifierName(nameof(UIntPtr)); internal static readonly AttributeSyntax ComImportAttributeSyntax = Attribute(IdentifierName("ComImport")); @@ -371,7 +372,7 @@ internal static ObjectCreationExpressionSyntax GuidValue(CustomAttribute guidAtt byte j = (byte)args.FixedArguments[9].Value!; byte k = (byte)args.FixedArguments[10].Value!; - return ObjectCreationExpression(IdentifierName(nameof(Guid))).AddArgumentListArguments( + return ObjectCreationExpression(GuidTypeSyntax).AddArgumentListArguments( Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(a), a))), Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(b), b))), Argument(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ToHex(c), c))), @@ -415,7 +416,7 @@ internal static ExpressionSyntax ToHexExpressionSyntax(MetadataReader reader, Co ExpressionSyntax UncheckedSignedWrapper(LiteralExpressionSyntax value, SyntaxKind signedType) { - return assignableToSignedInteger && value.Token.Text.StartsWith("0xF", StringComparison.OrdinalIgnoreCase) + return assignableToSignedInteger && char.ToUpper(value.Token.Text[2]) is '8' or '9' or (>= 'A' and <= 'F') ? UncheckedExpression(CastExpression(PredefinedType(Token(signedType)), value)) : value; } @@ -453,4 +454,24 @@ static ExpressionSyntax FloatExpression(float value) LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(value)); } } + + internal static ExpressionSyntax ToExpressionSyntax(PrimitiveTypeCode primitiveTypeCode, ReadOnlyMemory valueAsString) + { + string valueAsStringReally = valueAsString.ToString(); + return primitiveTypeCode switch + { + PrimitiveTypeCode.Int64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(long.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.Byte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(byte.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.SByte => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(sbyte.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.Int16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(short.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.UInt16 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ushort.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.Int32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(int.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.UInt32 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(uint.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.UInt64 => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(ulong.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.Single => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(float.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.Double => LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(double.Parse(valueAsStringReally, CultureInfo.InvariantCulture))), + PrimitiveTypeCode.String => LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(valueAsStringReally)), + _ => throw new NotSupportedException($"Unrecognized primitive type code: {primitiveTypeCode}."), + }; + } } diff --git a/test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs b/test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs new file mode 100644 index 00000000..3683886e --- /dev/null +++ b/test/Microsoft.Windows.CsWin32.Tests/ConstantsTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +public class ConstantsTests : GeneratorTestBase +{ + public ConstantsTests(ITestOutputHelper logger) + : base(logger) + { + } + + [Theory] + [InlineData("SECURITY_NULL_SID_AUTHORITY")] // SID_IDENTIFIER_AUTHORITY with byte[6] inline array + [InlineData("g_wszStreamBufferRecordingDuration")] // string + [InlineData("HWND_BOTTOM")] // A constant typed as a typedef'd struct + [InlineData("D2D1_DEFAULT_FLATTENING_TOLERANCE")] // a float constant + [InlineData("WIA_CATEGORY_FINISHED_FILE")] // GUID constant + [InlineData("DEVPKEY_MTPBTH_IsConnected")] // DEVPROPKEY constant + [InlineData("PKEY_AudioEndpoint_FormFactor")] // PROPERTYKEY constant + [InlineData("X509_CERT")] // A constant defined as PCSTR + [InlineData("RT_CURSOR")] // PCWSTR constant + [InlineData("HBMMENU_POPUP_RESTORE")] // A HBITMAP handle as a constant + + public void InterestingConstants(string name) + { + this.compilation = this.compilation.WithOptions(this.compilation.Options.WithPlatform(Platform.X64)); + this.generator = this.CreateGenerator(); + Assert.True(this.generator.TryGenerate(name, CancellationToken.None)); + this.CollectGeneratedCode(this.generator); + this.AssertNoDiagnostics(); + } +} diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs index db195bc1..f71d3d67 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs @@ -175,11 +175,9 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "JsRuntimeVersion", // An enum that has an extra member in a separate header file. "ReportEvent", // Failed at one point "DISPLAYCONFIG_VIDEO_SIGNAL_INFO", // Union, explicit layout, bitmask, nested structs - "g_wszStreamBufferRecordingDuration", // Constant string field "MFVideoAlphaBitmap", // field named params "DDRAWI_DDVIDEOPORT_INT", // field that is never used "MainAVIHeader", // dwReserved field is a fixed length array - "HBMMENU_POPUP_RESTORE", // A HBITMAP handle as a constant "RpcServerRegisterIfEx", // Optional attribute on delegate type. "RpcSsSwapClientAllocFree", // Parameters typed as pointers to in delegates and out delegates "RPC_DISPATCH_TABLE", // Struct with a field typed as a delegate @@ -187,7 +185,6 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "DDHAL_DESTROYDRIVERDATA", // Struct with a field typed as a delegate "I_RpcServerInqAddressChangeFn", // p/invoke that returns a function pointer "WSPUPCALLTABLE", // a delegate with a delegate in its signature - "HWND_BOTTOM", // A constant typed as a typedef'd struct "BOOL", // a special cased typedef struct "uregex_getMatchCallback", // friendly overload with delegate parameter, and out parameters "CreateDispatcherQueueController", // References a WinRT type @@ -208,11 +205,6 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "PICTYPE", // An enum with -1 as an enum value "CoCreateInstance", // a hand-written friendly overload "JsVariantToValue", - "D2D1_DEFAULT_FLATTENING_TOLERANCE", // a float constant - "WIA_CATEGORY_FINISHED_FILE", // GUID constant - "DEVPKEY_MTPBTH_IsConnected", // DEVPROPKEY constant - "PKEY_AudioEndpoint_FormFactor", // PROPERTYKEY constant - "RT_CURSOR", // PCWSTR constant "IOleUILinkContainerW", // An IUnknown-derived interface with no GUID "FILE_TYPE_NOTIFICATION_INPUT", "DS_SELECTION_LIST", // A struct with a fixed-length inline array of potentially managed structs @@ -222,7 +214,6 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "IDelayedPropertyStoreFactory", // interface inheritance across namespaces "D3D9ON12_ARGS", // Contains an inline array of IUnknown objects "NCryptOpenKey", // Generates a SafeHandle based on a UIntPtr - "X509_CERT", // A constant defined as PCSTR "CIDLData_CreateFromIDArray", // Method with out parameter of a possibly marshaled interop type shared with the BCL, "ID3D12Resource", // COM interface with base types "OpenTrace", // the CloseTrace method called by the SafeHandle returns WIN32_ERROR. The handle is ALWAYS 64-bits. From c7449af1fc9b9b30121bc04f515e0330fc887bf4 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 17 Mar 2023 09:52:04 -0600 Subject: [PATCH 5/5] Update WinRT ref assembly used by tests --- test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs | 5 +++-- test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs index 9218c3e3..3bd90926 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTestBase.cs @@ -301,8 +301,9 @@ protected async Task CreateCompilationAsync(ReferenceAssembli ImmutableArray metadataReferences = await references.ResolveAsync(LanguageNames.CSharp, default); // Workaround for https://github.com/dotnet/roslyn-sdk/issues/699 + const string winRTPackageId = "Microsoft.Windows.SDK.Contracts"; metadataReferences = metadataReferences.AddRange( - Directory.GetFiles(Path.Combine(Path.GetTempPath(), "test-packages", "Microsoft.Windows.SDK.Contracts.10.0.19041.1", "ref", "netstandard2.0"), "*.winmd").Select(p => MetadataReference.CreateFromFile(p))); + Directory.GetFiles(Path.Combine(Path.GetTempPath(), "test-packages", $"{winRTPackageId}.{references.Packages.Single(id => string.Equals(id.Id, winRTPackageId, StringComparison.OrdinalIgnoreCase)).Version}", "ref", "netstandard2.0"), "*.winmd").Select(p => MetadataReference.CreateFromFile(p))); // CONSIDER: How can I pass in the source generator itself, with AdditionalFiles, so I'm exercising that code too? var compilation = CSharpCompilation.Create( @@ -355,7 +356,7 @@ protected static class MyReferenceAssemblies { #pragma warning disable SA1202 // Elements should be ordered by access - because field initializer depend on each other private static readonly ImmutableArray AdditionalLegacyPackages = ImmutableArray.Create( - new PackageIdentity("Microsoft.Windows.SDK.Contracts", "10.0.19041.1")); + new PackageIdentity("Microsoft.Windows.SDK.Contracts", "10.0.22621.2")); private static readonly ImmutableArray AdditionalModernPackages = AdditionalLegacyPackages.AddRange(ImmutableArray.Create( new PackageIdentity("System.Runtime.CompilerServices.Unsafe", "6.0.0"), diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs index f71d3d67..154b1fe6 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs @@ -152,6 +152,7 @@ public void SupportedOSPlatform_AppearsOnFriendlyOverloads() "PZZWSTR", "PCZZSTR", "PCZZWSTR", + "RoCreatePropertySetSerializer", // References a WinRT API "LocalLock", // returns HLOCAL, which requires special release support "LoadLibraryEx", // method with a reserved parameter "IEnumNetCfgComponent", // interface with a method containing an `[Reserved] out` parameter (bonkers, I know).