diff --git a/src/Microsoft.Windows.CsWin32/Generator.cs b/src/Microsoft.Windows.CsWin32/Generator.cs index e0fe78f8..69925c3e 100644 --- a/src/Microsoft.Windows.CsWin32/Generator.cs +++ b/src/Microsoft.Windows.CsWin32/Generator.cs @@ -3885,6 +3885,12 @@ private IEnumerable CreateCommonTypeDefMembers(Identifi // If this typedef struct represents a pointer, add an IsNull property. if (fieldType is IdentifierNameSyntax { Identifier: { Value: nameof(IntPtr) or nameof(UIntPtr) } }) { + // internal static readonly HWND Null; + yield return FieldDeclaration(VariableDeclaration(structName.WithTrailingTrivia(TriviaList(Space))) + .AddVariables(VariableDeclarator(IdentifierName("Null").Identifier))) + .AddModifiers(TokenWithSpace(SyntaxKind.InternalKeyword), TokenWithSpace(SyntaxKind.StaticKeyword), TokenWithSpace(SyntaxKind.ReadOnlyKeyword)) + .WithSemicolonToken(SemicolonWithLineFeed); + // internal static bool IsNull => value == default; yield return PropertyDeclaration(PredefinedType(TokenWithSpace(SyntaxKind.BoolKeyword)), "IsNull") .AddModifiers(TokenWithSpace(this.Visibility)) diff --git a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs index 3b7c9866..bac747fb 100644 --- a/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs +++ b/test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs @@ -587,6 +587,17 @@ public void HandleStructsHaveIsNullProperty(string handleName) this.AssertGeneratedMember(handleName, "IsNull", "internal bool IsNull => Value == default;"); } + [Theory] + [InlineData("HANDLE")] + [InlineData("HGDIOBJ")] + [InlineData("HINSTANCE")] + public void HandleStructsHaveStaticNullField(string handleName) + { + // A null HGDIOBJ has a specific meaning beyond just the concept of an invalid handle: + // https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject#return-value + this.AssertGeneratedMember(handleName, "Null", "Null"); + } + [Theory] [InlineData("HANDLE")] [InlineData("HGDIOBJ")] @@ -598,7 +609,7 @@ public void HandleTypeDefsUseIntPtrAsFieldType(string handleType) this.CollectGeneratedCode(this.generator); this.AssertNoDiagnostics(); StructDeclarationSyntax hwnd = Assert.IsType(this.FindGeneratedType(handleType).Single()); - FieldDeclarationSyntax field = hwnd.Members.OfType().Single(); + FieldDeclarationSyntax field = hwnd.Members.OfType().First(); Assert.Equal(nameof(IntPtr), Assert.IsType(field.Declaration.Type).Identifier.ValueText); } @@ -1969,6 +1980,7 @@ namespace Foundation { internal readonly IntPtr Value; internal HWND(IntPtr value) => this.Value = value; + internal static readonly HWND Null; internal bool IsNull => Value == default; public static implicit operator IntPtr(HWND value) => value.Value; @@ -2075,6 +2087,7 @@ namespace Graphics.Gdi { internal readonly IntPtr Value; internal HDC(IntPtr value) => this.Value = value; + internal static readonly HDC Null; internal bool IsNull => Value == default; public static implicit operator IntPtr(HDC value) => value.Value; @@ -2117,6 +2130,7 @@ namespace Foundation { internal readonly IntPtr Value; internal HWND(IntPtr value) => this.Value = value; + internal static readonly HWND Null; internal bool IsNull => Value == default; public static implicit operator IntPtr(HWND value) => value.Value; @@ -2458,6 +2472,7 @@ namespace Foundation { internal readonly IntPtr Value; internal HANDLE(IntPtr value) => this.Value = value; + internal static readonly HANDLE Null; internal bool IsNull => Value == default; public static implicit operator IntPtr(HANDLE value) => value.Value;