-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Introduce AOT friendly version of ToLogFont and FromLogFont #70224
Conversation
These methods used by WinForms
Tagging subscribers to this area: @dotnet/area-system-drawing Issue DetailsThese methods used by WinForms
|
/cc @RussKie in case public surface should be modified to be able to use this change. |
Interop.User32.LOGFONT nativeLogFont = ToLogFontInternal(graphics); | ||
|
||
// PtrToStructure requires that the passed in object not be a value type. | ||
if (!type.IsValueType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove the type
variable and use typeof(T).IsValueType
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do even better, and put constraints on T
to be struct
. I do not think it's reasonable to allow objects if you are caring about trimmability.
GCHandle handle = GCHandle.Alloc(logFont, GCHandleType.Pinned); | ||
Buffer.MemoryCopy(&nativeLogFont, (byte*)handle.AddrOfPinnedObject(), nativeSize, nativeSize); | ||
handle.Free(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try finally to ensure that free is called?
/// </summary> | ||
/// <param name="lf">A boxed LOGFONT.</param> | ||
/// <returns>The newly created <see cref="Font"/>.</returns> | ||
public static Font FromLogFont<T>(T lf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lf
-> logFont
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. It's easy to be lazy.
@@ -291,6 +291,34 @@ public unsafe void ToLogFont(object logFont, Graphics graphics) | |||
} | |||
} | |||
|
|||
public unsafe void ToLogFont<T>(T logFont, Graphics graphics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't you changing the public API surface here? Shouldn't this go through a API design review meeting? System.Drawing.Common ships as a package which doesn't contain a contract assembly and hence customers would start seeing this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I'm not much experienced in this process, so decide to put some code. Should I create separate issue for API proposal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the API review process: https://github.com/dotnet/runtime/blob/main/docs/project/api-review-process.md.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public sealed partial class Font | ||
{ | ||
public void ToLogFont<T>(ref T logFont, Graphics graphics) where T: struct { throw null; } | ||
public static Font FromLogFont<T>(in T logFont) where T: struct { throw null; } | ||
public static Font FromLogFont<T>(in T logFont, IntPtr hdc) where T: struct { throw null; } | ||
public void ToLogFont<T>(ref T logFont) where T: struct { throw null; } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be conditionalised with #if NET7_0_OR_GREATER
?
/__w/1/s/.packages/microsoft.dotnet.apicompat/7.0.0-beta.22255.2/build/Microsoft.DotNet.ApiCompat.targets(52,5): error : Compat issues with assembly System.Drawing.Common: [/__w/1/s/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj]
##[error].packages/microsoft.dotnet.apicompat/7.0.0-beta.22255.2/build/Microsoft.DotNet.ApiCompat.targets(52,5): error : (NETCORE_ENGINEERING_TELEMETRY=Build) Compat issues with assembly System.Drawing.Common:
/__w/1/s/.packages/microsoft.dotnet.apicompat/7.0.0-beta.22255.2/build/Microsoft.DotNet.ApiCompat.targets(52,5): error : MembersMustExist : Member 'public System.Drawing.Font System.Drawing.Font.FromLogFont<T>(T)' does not exist in the implementation but it does exist in the contract. [/__w/1/s/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj]
##[error].packages/microsoft.dotnet.apicompat/7.0.0-beta.22255.2/build/Microsoft.DotNet.ApiCompat.targets(52,5): error : (NETCORE_ENGINEERING_TELEMETRY=Build) MembersMustExist : Member 'public System.Drawing.Font System.Drawing.Font.FromLogFont<T>(T)' does not exist in the implementation but it does exist in the contract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to enable the new APIs only for net7.0 (in both the ref and impl) instead of net6.0 and upwards?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simply think that adding new API to .Net 7 would be less of a problem then anything else. Nothing prevent this API to work in .NET 6,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I as a customer intentionally upgrade my System.Drawing.Common package reference from 6.0.0 to 7.0.0, I would expect that I get new features irrelevant of the underlying target framework that is being used. I think it makes sense to improve both the net6.0 and the net7.0 assets if there's no underlying reason to not do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's good point. I forget about that scenario, even if I use it all the time.
Interop.User32.LOGFONT nativeLogFont = ToLogFontInternal(graphics); | ||
|
||
// PtrToStructure requires that the passed in object not be a value type. | ||
Marshal.PtrToStructure<T>(new IntPtr(&nativeLogFont), logFont); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be best if these new APIs did not depend on the built-in runtime marshalling support at all.
Can these new APIs take void*
that points to LOGFONT
and require the callers to take of the marshalling if there is any marshalling required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would change like this logFont = *(T*)&nativeLogFont;
works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I get why you propose void*
, because any struct which given to method can have managed fields, then I cannot copy data over layout. @RussKie can we just adopt these small methods to WinForms in appropriate places, having new void*
methods in System.Drawing.Common
are probably not worth goal to pursue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just adopt these small methods to WinForms in appropriate places, having new
void*
methods inSystem.Drawing.Common
are probably not worth goal to pursue
We can surely consider that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looping in @JeremyKuhne as he may have thoughts on the subject as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we considered the unmanaged
constraint? Then you can do everything without Marshal
. @jkotas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unmanaged
constraint works for me.
ArgumentNullException.ThrowIfNull(logFont); | ||
|
||
int nativeSize = sizeof(Interop.User32.LOGFONT); | ||
if (Marshal.SizeOf<T>() != nativeSize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (Marshal.SizeOf<T>() != nativeSize) | |
if (sizeof(T) != nativeSize) |
It is not correct to use Marshal.SizeOf
here
Interop.User32.LOGFONT nativeLogFont = ToLogFontInternal(graphics); | ||
|
||
// PtrToStructure requires that the passed in object not be a value type. | ||
logFont = *(T*)&nativeLogFont; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interop.User32.LOGFONT nativeLogFont = ToLogFontInternal(graphics); | |
// PtrToStructure requires that the passed in object not be a value type. | |
logFont = *(T*)&nativeLogFont; | |
*(Interop.User32.LOGFONT*)&logFont = ToLogFontInternal(graphics); |
Avoid extra copy
} | ||
|
||
int nativeSize = sizeof(Interop.User32.LOGFONT); | ||
if (Marshal.SizeOf<T>() != nativeSize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
// Now that we know the marshalled size is the same as LOGFONT, copy in the data | ||
nativeLogFont = default; | ||
|
||
*(T*)&nativeLogFont = logFont; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this one.
Needs the API proposal to be approved first. |
I'm going to close this PR pending API approval. (This is what we generally do. It's easy to reopen the PR when API is approved.) |
Fixes dotnet/winforms#8846
These methods used by WinForms