Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SafeHandle marshalling #133

Merged
merged 8 commits into from
Oct 5, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net5.0</TargetFramework>
<LangVersion>8.0</LangVersion>
<RootNamespace>System.Runtime.InteropServices</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable


namespace System.Runtime.InteropServices
{
// [TODO] Remove once the attribute has been added to the BCL
Expand Down
29 changes: 29 additions & 0 deletions DllImportGenerator/Ancillary.Interop/MarshalEx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

using System.Reflection;

namespace System.Runtime.InteropServices
{
/// <summary>
/// Marshalling helper methods that will likely live in S.R.IS.Marshal
/// when we integrate our APIs with dotnet/runtime.
/// </summary>
public static class MarshalEx
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
public static TSafeHandle CreateSafeHandle<TSafeHandle>()
where TSafeHandle : SafeHandle
{
if (typeof(TSafeHandle).IsAbstract || typeof(TSafeHandle).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance, null, Type.EmptyTypes, null) == null)
{
throw new MissingMemberException($"The safe handle type '{typeof(TSafeHandle).FullName}' must be a non-abstract type with a parameterless constructor.");
}

TSafeHandle safeHandle = (TSafeHandle)Activator.CreateInstance(typeof(TSafeHandle), nonPublic: true)!;
return safeHandle;
}

public static void SetHandle(SafeHandle safeHandle, IntPtr handle)
{
typeof(SafeHandle).GetMethod("SetHandle", BindingFlags.NonPublic | BindingFlags.Instance)!.Invoke(safeHandle, new object[] { handle });
}
}
}
4 changes: 4 additions & 0 deletions DllImportGenerator/Demo/Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
<ProjectReference Include="..\TestAssets\NativeExports\NativeExports.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="xunit" Version="$(XunitVersion)" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions DllImportGenerator/Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ static void Main(string[] args)
c = b;
NativeExportsNE.Sum(a, ref c);
Console.WriteLine($"{a} + {b} = {c}");

SafeHandleTests tests = new SafeHandleTests();

tests.ReturnValue_CreatesSafeHandle();
tests.ByValue_CorrectlyUnwrapsHandle();
tests.ByRefSameValue_UsesSameHandleInstance();
tests.ByRefDifferentValue_UsesNewHandleInstance();
}
}
}
74 changes: 74 additions & 0 deletions DllImportGenerator/Demo/SafeHandleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using Xunit;

namespace Demo
{
partial class NativeExportsNE
{
public class NativeExportsSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private NativeExportsSafeHandle() : base(true)
{
}

protected override bool ReleaseHandle()
{
Assert.True(NativeExportsNE.ReleaseHandle(handle));
return true;
}
}

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "alloc_handle")]
public static partial NativeExportsSafeHandle AllocateHandle();

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "release_handle")]
[return:MarshalAs(UnmanagedType.I1)]
private static partial bool ReleaseHandle(nint handle);

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "is_handle_alive")]
[return:MarshalAs(UnmanagedType.I1)]
public static partial bool IsHandleAlive(NativeExportsSafeHandle handle);

[GeneratedDllImport(nameof(NativeExportsNE), EntryPoint = "modify_handle")]
public static partial void ModifyHandle(ref NativeExportsSafeHandle handle, [MarshalAs(UnmanagedType.I1)] bool newHandle);
}

public class SafeHandleTests
{
[Fact]
public void ReturnValue_CreatesSafeHandle()
{
using NativeExportsNE.NativeExportsSafeHandle handle = NativeExportsNE.AllocateHandle();
Assert.False(handle.IsClosed);
Assert.False(handle.IsInvalid);
}

[Fact]
public void ByValue_CorrectlyUnwrapsHandle()
{
using NativeExportsNE.NativeExportsSafeHandle handle = NativeExportsNE.AllocateHandle();
Assert.True(NativeExportsNE.IsHandleAlive(handle));
}

[Fact]
public void ByRefSameValue_UsesSameHandleInstance()
{
using NativeExportsNE.NativeExportsSafeHandle handleToDispose = NativeExportsNE.AllocateHandle();
NativeExportsNE.NativeExportsSafeHandle handle = handleToDispose;
NativeExportsNE.ModifyHandle(ref handle, false);
Assert.Same(handleToDispose, handle);
}

[Fact]
public void ByRefDifferentValue_UsesNewHandleInstance()
{
using NativeExportsNE.NativeExportsSafeHandle handleToDispose = NativeExportsNE.AllocateHandle();
NativeExportsNE.NativeExportsSafeHandle handle = handleToDispose;
NativeExportsNE.ModifyHandle(ref handle, true);
Assert.NotSame(handleToDispose, handle);
handle.Dispose();
}
}
}
1 change: 1 addition & 0 deletions DllImportGenerator/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<CompilerPlatformVersion>3.8.0-3.final</CompilerPlatformVersion>
<XunitVersion>2.4.1</XunitVersion>
</PropertyGroup>

</Project>
1 change: 1 addition & 0 deletions DllImportGenerator/DllImportGenerator.Test/Compiles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()
yield return new[] { CodeSnippets.DelegateMarshalAsParametersAndModifiers };
yield return new[] { CodeSnippets.BlittableStructParametersAndModifiers };
yield return new[] { CodeSnippets.GenericBlittableStructParametersAndModifiers };
yield return new[] { CodeSnippets.BasicParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle") };
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ internal class MarshallingGenerators
public static readonly Forwarder Forwarder = new Forwarder();
public static readonly BlittableMarshaller Blittable = new BlittableMarshaller();
public static readonly DelegateMarshaller Delegate = new DelegateMarshaller();
public static readonly SafeHandleMarshaller SafeHandle = new SafeHandleMarshaller();

public static bool TryCreate(TypePositionInfo info, StubCodeContext context, out IMarshallingGenerator generator)
{
Expand Down Expand Up @@ -126,6 +127,10 @@ public static bool TryCreate(TypePositionInfo info, StubCodeContext context, out
generator = Forwarder;
return false;

case { MarshallingAttributeInfo: SafeHandleMarshallingInfo _}:
generator = SafeHandle;
return true;

default:
generator = Forwarder;
return false;
Expand Down
Loading