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
4 changes: 2 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.Handle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ public partial class Generator
{
// protected override [unsafe] bool ReleaseHandle() => ReleaseMethod((struct)this.handle);
// Special case release functions based on their return type as follows: (https://github.com/microsoft/win32metadata/issues/25)
// * bool => true is success
// * bool, BOOL, BOOLEAN => true (non-zero) is success
// * int => zero is success
// * uint => zero is success
// * byte => non-zero is success
Expand All @@ -226,7 +226,7 @@ public partial class Generator
// As an example of that see WlanCloseHandle function.
var releaseMethodIsUnsafe = releaseMethodHasReservedParameters;
if (!(releaseMethodReturnType.Type is PredefinedTypeSyntax { Keyword: { RawKind: (int)SyntaxKind.BoolKeyword } } ||
releaseMethodReturnType.Type is QualifiedNameSyntax { Right: { Identifier: { ValueText: "BOOL" } } }))
releaseMethodReturnType.Type is QualifiedNameSyntax { Right: { Identifier: { ValueText: "BOOL" or "BOOLEAN" } } }))
{
switch (releaseMethodReturnType.Type)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Windows.CsWin32/Generator.SafeHandleLease.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal bool TryGetResourceReleaseMethod(TypeHandleInfo valueType, CustomAttrib
private static bool IsSupportedCleanupReturn(TypeSyntax type, string method) => type switch
{
PredefinedTypeSyntax predefined => predefined.Keyword.Kind() is SyntaxKind.BoolKeyword or SyntaxKind.IntKeyword or SyntaxKind.UIntKeyword or SyntaxKind.ByteKeyword or SyntaxKind.VoidKeyword,
QualifiedNameSyntax { Right: IdentifierNameSyntax name } => name.Identifier.ValueText is "BOOL" or "NTSTATUS" or "HRESULT" or "WIN32_ERROR" or "CONFIGRET" or "HGLOBAL" or "HLOCAL"
QualifiedNameSyntax { Right: IdentifierNameSyntax name } => name.Identifier.ValueText is "BOOL" or "BOOLEAN" or "NTSTATUS" or "HRESULT" or "WIN32_ERROR" or "CONFIGRET" or "HGLOBAL" or "HLOCAL"
|| (name.Identifier.ValueText == "LRESULT" && method == "ICClose"),
PointerTypeSyntax { ElementType: PredefinedTypeSyntax predefined } => predefined.Keyword.IsKind(SyntaxKind.VoidKeyword) && method == "FreeSid",
_ => false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ ServiceFabric.winmd | [youyuanwu/fabric-metadata](https://github.com/youyuanwu/f
CustomIInspectable.winmd | Generated using WinMDGenerator toolchain. Project in the subdirectory [CustomIInspectable](../../CustomIInspectable) with build instructions in [readme.md](../../CustomIInspectable/readme.md).
WnfWithoutStatusSuccess.winmd | Authored fixture for issue #1677 regression coverage. Its source is checked in as [WnfWithoutStatusSuccess.il](WnfWithoutStatusSuccess.il). We keep this one as IL because that is much easier to maintain than reproducing the full end-to-end WinmdGenerator workflow for a focused test asset.
SelfContained.winmd | Authored fixture reproducing the OSClient single-`Generator` build configuration (a non-`Windows.Win32` metadata assembly with the Windows.Win32 projection provided by a reference), where special string typedefs (`PCWSTR`) must be referenced under `Windows.Win32.Foundation`. Its source is checked in as [SelfContained.il](SelfContained.il). Kept as IL for the same reason as WnfWithoutStatusSuccess.winmd.
SafeHandleOwnership.winmd | Authored fixture for contextual `RAIIFree` and `FreeWith` ownership, HANDLE input inference without type-level ownership, exact native value types, and caller-defined cleanup. Its source is checked in as [SafeHandleOwnership.il](SafeHandleOwnership.il), including the IL assembler command. Tests load this asset rather than emitting metadata at runtime.
SafeHandleOwnership.winmd | Authored fixture for contextual `RAIIFree` and `FreeWith` ownership, HANDLE input inference without type-level ownership, exact native value types, caller-defined cleanup, and cleanup functions that return `BOOLEAN`. Its source is checked in as [SafeHandleOwnership.il](SafeHandleOwnership.il), including the IL assembler command. Tests load this asset rather than emitting metadata at runtime.
FlatNamespace.winmd | Authored fixture for issue #1848 regression coverage, with native typedefs and associated enums at the common namespace root and in a nested namespace. Its source and regeneration command are checked in as [FlatNamespace.il](FlatNamespace.il).
TypeDefConstant.winmd | Authored fixture verifying that struct-typed constants defined in the same metadata module using TypeDefinition handles (rather than TypeReference handles) correctly nest into the struct and attach to extension blocks. Its source is checked in as [TypeDefConstant.il](TypeDefConstant.il). Kept as IL for the same reason as WnfWithoutStatusSuccess.winmd.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// Contextual ownership without type-level RAIIFree, including exact native types,
// incompatible cleanup, and cleanup requiring caller-supplied context.
// incompatible cleanup, cleanup requiring caller-supplied context, and cleanup returning BOOLEAN.
// Assemble with: ilasm /dll /output=SafeHandleOwnership.winmd SafeHandleOwnership.il

.assembly extern netstandard { .publickeytoken = (CC 7B 13 FF CD 2D DD 51) .ver 2:1:0:0 }
Expand All @@ -29,6 +29,13 @@
.field public void* Value
}

.class public sequential ansi sealed beforefieldinit Windows.Win32.Foundation.BOOLEAN
extends [netstandard]System.ValueType
{
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.NativeTypedefAttribute::.ctor() = (01 00 00 00)
.field public uint8 Value
}

.class public sequential ansi sealed beforefieldinit Windows.Win32.Test.RESOURCE_A
extends [netstandard]System.ValueType
{
Expand Down Expand Up @@ -90,6 +97,8 @@
void FreeUnsigned(valuetype Windows.Win32.Test.UNSIGNED_RESOURCE resource) cil managed { }
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
void FreeUnsigned32(valuetype Windows.Win32.Test.UNSIGNED32_RESOURCE resource) cil managed { }
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
valuetype Windows.Win32.Foundation.BOOLEAN FreeBoolean(valuetype Windows.Win32.Test.RESOURCE_A resource) cil managed { }
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
void ConsumeResource([in] valuetype Windows.Win32.Test.RESOURCE_A resource) cil managed { }
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
Expand Down Expand Up @@ -171,6 +180,18 @@
.param [0]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.RAIIFreeAttribute::.ctor(string) = (01 00 0A 46 72 65 65 53 68 61 72 65 64 00 00)
}
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
valuetype Windows.Win32.Test.RESOURCE_A CreateBooleanResource() cil managed
{
.param [0]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.RAIIFreeAttribute::.ctor(string) = (01 00 0B 46 72 65 65 42 6F 6F 6C 65 61 6E 00 00)
}
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
valuetype Windows.Win32.Foundation.BOOLEAN AcquireBooleanResource([out] valuetype Windows.Win32.Test.RESOURCE_A* resource) cil managed
{
.param [1]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.RAIIFreeAttribute::.ctor(string) = (01 00 0B 46 72 65 65 42 6F 6F 6C 65 61 6E 00 00)
}
}

.class public abstract auto autochar sealed beforefieldinit Windows.Win32.Test.FreeWith.Apis
Expand Down Expand Up @@ -242,4 +263,16 @@
.param [0]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.FreeWithAttribute::.ctor(string) = (01 00 0A 46 72 65 65 53 68 61 72 65 64 00 00)
}
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
valuetype Windows.Win32.Test.RESOURCE_A CreateBooleanResource() cil managed
{
.param [0]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.FreeWithAttribute::.ctor(string) = (01 00 0B 46 72 65 65 42 6F 6F 6C 65 61 6E 00 00)
}
.method public hidebysig static pinvokeimpl("fixture.dll" nomangle winapi)
valuetype Windows.Win32.Foundation.BOOLEAN AcquireBooleanResource([out] valuetype Windows.Win32.Test.RESOURCE_A* resource) cil managed
{
.param [1]
.custom instance void [Windows.Win32.winmd]Windows.Win32.Foundation.Metadata.FreeWithAttribute::.ctor(string) = (01 00 0B 46 72 65 65 42 6F 6F 6C 65 61 6E 00 00)
}
}
Binary file not shown.
38 changes: 38 additions & 0 deletions test/Microsoft.Windows.CsWin32.Tests/SafeHandleLeaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,44 @@ public void IncompatibleCleanupDoesNotProduceOwner(string attribute)
Assert.Single(this.FindGeneratedMethod("FreeOtherResource"));
}

/// <summary>
/// Verifies that a cleanup function returning <c>BOOLEAN</c> produces a constructible owner for returned and output resources.
/// </summary>
/// <param name="attribute">The contextual cleanup attribute.</param>
/// <param name="allowMarshaling">Whether runtime marshaling is enabled.</param>
[Theory, CombinatorialData]
public void BooleanCleanupProducesOwner(
[CombinatorialValues("RAIIFree", "FreeWith")] string attribute, bool allowMarshaling)
{
this.GenerateFixture(attribute, allowMarshaling, "CreateBooleanResource", "AcquireBooleanResource");
this.compilation = this.AddCode("""
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Test;
static class Usage
{
static void Use()
{
using FreeBooleanSafeHandle created = PInvoke.CreateBooleanResource_SafeHandle();
BOOLEAN acquiredResult = PInvoke.AcquireBooleanResource(out FreeBooleanSafeHandle acquired);
using (acquired)
{
using var lease = acquired.Lease();
RESOURCE_A value = lease.Value;
}
}
}
""");
this.AssertNoDiagnostics(logAllGeneratedCode: false);
INamedTypeSymbol? owner = this.compilation.GetTypeByMetadataName("Windows.Win32.FreeBooleanSafeHandle");
Assert.NotNull(owner);
Assert.False(owner.IsAbstract);
TypeDeclarationSyntax ownerDeclaration = Assert.IsAssignableFrom<TypeDeclarationSyntax>(Assert.Single(this.FindGeneratedType("FreeBooleanSafeHandle")));
MethodDeclarationSyntax releaseHandle = Assert.Single(ownerDeclaration.Members.OfType<MethodDeclarationSyntax>(), method => method.Identifier.ValueText == "ReleaseHandle");
InvocationExpressionSyntax release = Assert.IsType<InvocationExpressionSyntax>(releaseHandle.ExpressionBody?.Expression);
Assert.Equal("FreeBoolean", Assert.IsType<MemberAccessExpressionSyntax>(release.Expression).Name.Identifier.ValueText);
}

/// <summary>
/// Verifies that annotated cleanup functions remain callable when ownership projection is unavailable.
/// </summary>
Expand Down
Loading