Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\NativeMarshallingAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\PointerArrayMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\ReadOnlySpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\SafeHandleMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\SpanMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf16StringMarshaller.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshalling\Utf8StringMarshaller.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace System.Runtime.InteropServices.Marshalling
{
/// <summary>
/// A marshaller for <see cref="SafeHandle"/>-derived types that marshals the handle following the lifetime rules for <see cref="SafeHandle"/>s.
/// </summary>
/// <typeparam name="T">The <see cref="SafeHandle"/>-derived type.</typeparam>
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder), MarshalMode.ManagedToUnmanagedIn, typeof(SafeHandleMarshaller<>.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder), MarshalMode.ManagedToUnmanagedRef, typeof(SafeHandleMarshaller<>.ManagedToUnmanagedRef))]
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder), MarshalMode.ManagedToUnmanagedOut, typeof(SafeHandleMarshaller<>.ManagedToUnmanagedOut))]
public static class SafeHandleMarshaller<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T> where T : SafeHandle
{
/// <summary>
/// Custom marshaller to marshal a <see cref="SafeHandle"/> as its underlying handle value.
/// </summary>
public struct ManagedToUnmanagedIn
{
private bool _addRefd;
private T? _handle;

/// <summary>
/// Initializes the marshaller from a managed handle.
/// </summary>
/// <param name="handle">The managed handle.</param>
public void FromManaged(T handle)
{
_handle = handle;
handle.DangerousAddRef(ref _addRefd);
}

/// <summary>
/// Get the unmanaged handle.
/// </summary>
/// <returns>The unmanaged handle.</returns>
public IntPtr ToUnmanaged() => _handle!.DangerousGetHandle();

/// <summary>
/// Release any references keeping the managed handle alive.
/// </summary>
public void Free()
{
if (_addRefd)
{
_handle!.DangerousRelease();
}
}
}


/// <summary>
/// Custom marshaller to marshal a <see cref="SafeHandle"/> as its underlying handle value.
/// </summary>
public struct ManagedToUnmanagedRef
{
private bool _addRefd;
private bool _callInvoked;
private T? _handle;
private IntPtr _originalHandleValue;
private T _newHandle;
private T? _handleToReturn;

/// <summary>
/// Create the marshaller in a default state.
/// </summary>
public ManagedToUnmanagedRef()
{
_addRefd = false;
_callInvoked = false;
_newHandle = (T)Activator.CreateInstance(typeof(T), nonPublic: true)!;
}

/// <summary>
/// Initialize the marshaller from a managed handle.
/// </summary>
/// <param name="handle">The managed handle</param>
public void FromManaged(T handle)
{
_handle = handle;
handle.DangerousAddRef(ref _addRefd);
_originalHandleValue = handle.DangerousGetHandle();
}

/// <summary>
/// Retrieve the unmanaged handle.
/// </summary>
/// <returns>The unmanaged handle</returns>
public IntPtr ToUnmanaged() => _originalHandleValue;

/// <summary>
/// Initialize the marshaller from an unmanaged handle.
/// </summary>
/// <param name="value">The unmanaged handle.</param>
public void FromUnmanaged(IntPtr value)
{
if (value == _originalHandleValue)
{
_handleToReturn = _handle;
}
else
{
Marshal.InitHandle(_newHandle, value);
_handleToReturn = _newHandle;
}
}

/// <summary>
/// Notify the marshaller that the native call has been invoked.
/// </summary>
public void OnInvoked()
{
_callInvoked = true;
}

/// <summary>
/// Retrieve the managed handle from the marshaller.
/// </summary>
/// <returns>The managed handle.</returns>
public T ToManagedFinally() => _handleToReturn!;

/// <summary>
/// Free any resources and reference counts owned by the marshaller.
/// </summary>
public void Free()
{
if (_addRefd)
{
_handle!.DangerousRelease();
}

// If we never invoked the call, then we aren't going to use the
// new handle. Dispose it now to avoid clogging up the finalizer queue
// unnecessarily.
if (!_callInvoked)
{
_newHandle.Dispose();
}
}
}

/// <summary>
/// Custom marshaller to marshal a <see cref="SafeHandle"/> as its underlying handle value.
/// </summary>
public struct ManagedToUnmanagedOut
{
private bool _callInvoked;
private T _newHandle;

/// <summary>
/// Create the marshaller in a default state.
/// </summary>
public ManagedToUnmanagedOut()
{
_callInvoked = false;
_newHandle = (T)Activator.CreateInstance(typeof(T), nonPublic: true)!;
}

/// <summary>
/// Initialize the marshaller from an unmanaged handle.
/// </summary>
/// <param name="value">The unmanaged handle.</param>
public void FromUnmanaged(IntPtr value)
{
Marshal.InitHandle(_newHandle, value);
}

/// <summary>
/// Notify the marshaller that the native call has been invoked.
/// </summary>
public void OnInvoked()
{
_callInvoked = true;
}

/// <summary>
/// Retrieve the managed handle from the marshaller.
/// </summary>
/// <returns>The managed handle.</returns>
public T ToManaged() => _newHandle;

/// <summary>
/// Free any resources and reference counts owned by the marshaller.
/// </summary>
public void Free()
{
// If we never invoked the call, then we aren't going to use the
// new handle. Dispose it now to avoid clogging up the finalizer queue
// unnecessarily.
if (!_callInvoked)
{
_newHandle!.Dispose();
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeCont
}
break;
case StubCodeContext.Stage.NotifyForSuccessfulInvoke:
if (elementMarshalDirection is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional)
{
return _nativeTypeMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context);
}
break;
return _nativeTypeMarshaller.GenerateNotifyForSuccessfulInvokeStatements(info, context);
case StubCodeContext.Stage.UnmarshalCapture:
if (elementMarshalDirection is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</ItemGroup>

<ItemGroup>
<Compile Include="$(CommonPath)Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
<Compile Include="../../tests/Common/MarshalDirection.cs" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;

namespace Microsoft.Interop
{
Expand All @@ -19,11 +21,13 @@ public sealed record SafeHandleMarshallingInfo(bool AccessibleDefaultConstructor
public sealed class SafeHandleMarshallingInfoProvider : ITypeBasedMarshallingInfoProvider
{
private readonly Compilation _compilation;
private readonly INamedTypeSymbol _safeHandleMarshallerType;
private readonly ITypeSymbol _containingScope;

public SafeHandleMarshallingInfoProvider(Compilation compilation, ITypeSymbol containingScope)
{
_compilation = compilation;
_safeHandleMarshallerType = compilation.GetBestTypeByMetadataName(TypeNames.System_Runtime_InteropServices_Marshalling_SafeHandleMarshaller_Metadata);
_containingScope = containingScope;
}

Expand All @@ -47,18 +51,50 @@ public bool CanProvideMarshallingInfoForType(ITypeSymbol type)

public MarshallingInfo GetMarshallingInfo(ITypeSymbol type, int indirectionDepth, UseSiteAttributeProvider useSiteAttributes, GetMarshallingInfoCallback marshallingInfoCallback)
{
bool hasDefaultConstructor = false;
bool hasAccessibleDefaultConstructor = false;
if (type is INamedTypeSymbol named && !named.IsAbstract && named.InstanceConstructors.Length > 0)
{
foreach (IMethodSymbol ctor in named.InstanceConstructors)
{
if (ctor.Parameters.Length == 0)
{
hasDefaultConstructor = true;
hasAccessibleDefaultConstructor = _compilation.IsSymbolAccessibleWithin(ctor, _containingScope);
break;
}
}
}

if (_safeHandleMarshallerType is not null)
{

INamedTypeSymbol entryPointType = _safeHandleMarshallerType.Construct(type);
if (!ManualTypeMarshallingHelper.TryGetValueMarshallersFromEntryType(
entryPointType,
type,
_compilation,
out CustomTypeMarshallers? marshallers))
{
return NoMarshallingInfo.Instance;
}

// If the SafeHandle-derived type doesn't have a default constructor or is abstract,
// we only support managed-to-unmanaged marshalling
if (!hasDefaultConstructor || type.IsAbstract)
{
marshallers = marshallers.Value with
{
Modes = ImmutableDictionary<MarshalMode, CustomTypeMarshallerData>.Empty
.Add(
MarshalMode.ManagedToUnmanagedIn,
marshallers.Value.GetModeOrDefault(MarshalMode.ManagedToUnmanagedIn))
};
}

return new NativeMarshallingAttributeInfo(ManagedTypeInfo.CreateTypeInfoForTypeSymbol(entryPointType), marshallers.Value);
}

return new SafeHandleMarshallingInfo(hasAccessibleDefaultConstructor, type.IsAbstract);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,7 @@ public static string MarshalEx(InteropGenerationOptions options)
public const string GeneratedComClassAttribute = "System.Runtime.InteropServices.Marshalling.GeneratedComClassAttribute";
public const string ComExposedClassAttribute = "System.Runtime.InteropServices.Marshalling.ComExposedClassAttribute";
public const string IComExposedClass = "System.Runtime.InteropServices.Marshalling.IComExposedClass";

public const string System_Runtime_InteropServices_Marshalling_SafeHandleMarshaller_Metadata = "System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller`1";
}
}
59 changes: 59 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13486,6 +13486,65 @@ public static class UnmanagedToManagedOut
public static System.Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) { throw null; }
}
}

[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute.GenericPlaceholder),
System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedIn,
typeof(System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<>.ManagedToUnmanagedIn))]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute.GenericPlaceholder),
System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedRef,
typeof(System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<>.ManagedToUnmanagedRef))]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute.GenericPlaceholder),
System.Runtime.InteropServices.Marshalling.MarshalMode.ManagedToUnmanagedOut,
typeof(System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller<>.ManagedToUnmanagedOut))]
public static class SafeHandleMarshaller<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors)] T> where T : SafeHandle
{
public struct ManagedToUnmanagedIn
{
private int _dummyPrimitive;
private T _handle;
public void FromManaged(T handle) { }

public nint ToUnmanaged() { throw null; }

public void Free() { }
}

public struct ManagedToUnmanagedRef
{
private int _dummyPrimitive;
private T _handle;

public ManagedToUnmanagedRef() { }

public void FromManaged(T handle) { }

public nint ToUnmanaged() { throw null; }

public void FromUnmanaged(nint value) { }

public void OnInvoked() { }

public T ToManagedFinally() { throw null; }

public void Free() { }
}

public struct ManagedToUnmanagedOut
{
private int _dummyPrimitive;
private T _newHandle;
public ManagedToUnmanagedOut() { }

public void FromUnmanaged(nint value) { }

public void OnInvoked() { }

public T ToManaged() { throw null; }

public void Free() { }
}
}

[System.CLSCompliant(false)]
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Span<>),
System.Runtime.InteropServices.Marshalling.MarshalMode.Default,
Expand Down