-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Add new SafeHandleMarshaller type to provide out-of-generator marshalling support. #85419
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
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
84ab7dc
Add new SafeHandleMarshaller type to provide out-of-generator marshal…
jkoritzinsky d4fa0c2
Always call OnInvoked on marshallers no matter the direction (as a re…
jkoritzinsky beae478
Don't use OnInvoked in the unmanaged->managed marshaller. Our shape d…
jkoritzinsky 0c81d9f
Add a test that ensures SafeHandle[]s aren't supported.
jkoritzinsky 0233e2a
Breaking change: Only support SafeHandle-derived types with public pa…
jkoritzinsky ea4fc5f
Change constructor visibility in LibraryImportGenerator.Tests SafeHan…
jkoritzinsky dfde41d
Move SafeHandle-with-private-constructor test to CompileFails.
jkoritzinsky c6e6c62
Update compatibility doc
jkoritzinsky File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
...em.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/SafeHandleMarshaller.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)] 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; | ||
// SafeHandle ref marshalling has always required parameterless constructors, | ||
// but it has never required them to be public. | ||
// We construct the handle now to ensure we don't cause an exception | ||
// before we are able to capture the unmanaged handle after the call. | ||
_newHandle = Activator.CreateInstance<T>()!; | ||
} | ||
|
||
/// <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 _initialized; | ||
private T _newHandle; | ||
|
||
/// <summary> | ||
/// Create the marshaller in a default state. | ||
/// </summary> | ||
public ManagedToUnmanagedOut() | ||
{ | ||
_initialized = false; | ||
// SafeHandle out marshalling has always required parameterless constructors, | ||
// but it has never required them to be public. | ||
// We construct the handle now to ensure we don't cause an exception | ||
// before we are able to capture the unmanaged handle after the call. | ||
_newHandle = Activator.CreateInstance<T>()!; | ||
} | ||
|
||
/// <summary> | ||
/// Initialize the marshaller from an unmanaged handle. | ||
/// </summary> | ||
/// <param name="value">The unmanaged handle.</param> | ||
public void FromUnmanaged(IntPtr value) | ||
{ | ||
_initialized = true; | ||
Marshal.InitHandle(_newHandle, value); | ||
} | ||
|
||
/// <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 captured the handle value, then we aren't going to use the | ||
// new handle. Dispose it now to avoid clogging up the finalizer queue | ||
// unnecessarily. | ||
if (!_initialized) | ||
{ | ||
_newHandle!.Dispose(); | ||
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.