Introduce AOT friendly version of ToLogFont and FromLogFont#70224
Introduce AOT friendly version of ToLogFont and FromLogFont#70224kant2002 wants to merge 12 commits intodotnet:mainfrom
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.
You can remove the type variable and use typeof(T).IsValueType here.
There was a problem hiding this comment.
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.
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.
Thank you. It's easy to be lazy.
| } | ||
| } | ||
|
|
||
| public unsafe void ToLogFont<T>(T logFont, Graphics graphics) |
There was a problem hiding this comment.
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.
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.
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.
| 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.
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.
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.
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.
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.
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.
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.
Would change like this logFont = *(T*)&nativeLogFont; works?
There was a problem hiding this comment.
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.
can we just adopt these small methods to WinForms in appropriate places, having new
void*methods inSystem.Drawing.Commonare probably not worth goal to pursue
We can surely consider that.
There was a problem hiding this comment.
Looping in @JeremyKuhne as he may have thoughts on the subject as well.
There was a problem hiding this comment.
Have we considered the unmanaged constraint? Then you can do everything without Marshal. @jkotas?
There was a problem hiding this comment.
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.
| 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.
| 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) |
| // 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.
Not sure about this one.
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
|
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