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

Move System.Drawing.Common to SafeHandles et. al. #63085

Closed
wants to merge 15 commits into from

Conversation

teo-tsirpanis
Copy link
Contributor

The interop code of System.Drawing.Common heavily uses the very old HandleRef type which predates the widely used and safer SafeHandles that .NET Framework 2.0 brought. Its use of this legacy type has become an issue since the P/Invoke generator doesn't support marshalling HandleRefs.

For this reason and to also improve the library's stability (SafeHandles are strongly-typed and harder to misuse and I have seen IntPtrs in P/Invoke signatures that should have been HandleRefs), this PR will replace all usages of HandleRef with SafeHandles.

I have created a (currently internal) subclass of SafeHandle called SafeGdiPlusHandle, which will be inherited by one class per GDI+ handle type (SafeBrushHandle, SafePenHandle etc). dotnet/winforms#8833 will need to be reconsidered and perhaps superseded by a new proposal that makes the safe handles public.

I will also fix other minor things I might encounter.

And fix some P/Invoke definitions.
@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Dec 22, 2021
@ghost
Copy link

ghost commented Dec 22, 2021

Tagging subscribers to this area: @dotnet/area-system-drawing
See info in area-owners.md if you want to be subscribed.

Issue Details

The interop code of System.Drawing.Common heavily uses the very old HandleRef type which predates the widely used and safer SafeHandles that .NET Framework 2.0 brought. Its use of this legacy type has become an issue since the P/Invoke generator doesn't support marshalling HandleRefs.

For this reason and to also improve the library's stability (SafeHandles are strongly-typed and harder to misuse and I have seen IntPtrs in P/Invoke signatures that should have been HandleRefs), this PR will replace all usages of HandleRef with SafeHandles.

I have created a (currently internal) subclass of SafeHandle called SafeGdiPlusHandle, which will be inherited by one class per GDI+ handle type (SafeBrushHandle, SafePenHandle etc). dotnet/winforms#8833 will need to be reconsidered and perhaps superseded by a new proposal that makes the safe handles public.

I will also fix other minor things I might encounter.

Author: teo-tsirpanis
Assignees: -
Labels:

area-System.Drawing, community-contribution

Milestone: -

@ghost ghost added this to Active PRs in ML, Extensions, Globalization, etc, POD. Dec 22, 2021
@jkoritzinsky
Copy link
Member

Looks like you need to add public parameterless constructors to each of the SafeHandle-derived types you're introducing in this PR.

@teo-tsirpanis
Copy link
Contributor Author

Thanks @jkoritzinsky, I had noticed it as well. I opened #63109 to suggest an analyzer that detects cases like that.

@teo-tsirpanis
Copy link
Contributor Author

The tests are failing because they are trying to access disposed handles. It used to fail with ArgumentException (no idea where it is thrown), but now it fails with ObjectDisposedException because SafeHandle.DangerousAddRef throws it for disposed handles. I will fix them later.

There will be a breaking change, but I think it is for the better; the overall improvements brought by the SafeHandle migration definitely outweigh it, and ObjectDisposedException is more fitting for accessing disposed resources.

@AraHaan
Copy link
Member

AraHaan commented Dec 24, 2021

What about your new HandleRef's in this code? Do you plan to make those SafeHandles as well?

@teo-tsirpanis
Copy link
Contributor Author

Yes @AraHaan, I was consolidating some interop code that had IntPtrs on Unix and of HandleRefs on Windows (don't see a reason why). Until I port these GDI+ object types to SafeHandles, they will temporarily use HandleRefs.

It's sealed and no longer has a finalizer. It does not need the full-blown Dispose pattern.
@AraHaan
Copy link
Member

AraHaan commented Dec 24, 2021

Is there plans to also backport these changes to 6.0 in a servicing release as well?

@teo-tsirpanis
Copy link
Contributor Author

Almost certainly not @AraHaan, this is quite a significant change, and the different exceptions thrown when accessing disposed objects will cause breaks.

Accessing a disposed handle now throws ObjectDisposedException instead of ArgumentException.
And rename some parameter names in the manually marshalled P/Invoke definitions.
@@ -78,7 +78,7 @@ public int NextSubpath(out int startIndex, out int endIndex, out bool isClosed)
public int NextSubpath(GraphicsPath path, out bool isClosed)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it have been a GraphicsPath? here and in a similar case below? Both APIs work when null is passed.

@@ -13,7 +13,7 @@ public sealed class GraphicsPathIterator : MarshalByRefObject, IDisposable
public GraphicsPathIterator(GraphicsPath? path)
{
IntPtr nativeIter = IntPtr.Zero;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be SafeGraphicsPathIteratorHandle?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't port it yet. The PR is still in progress and not yet ready for review.

@teo-tsirpanis
Copy link
Contributor Author

I will pause working on this until the custom DllImportGenerator marshallers become available. There are P/Invokes that need a nullable SafeHandle, which is not normally supported. I have been writing the marshalling code myself in GdiplusNative.ManualMarshalling.cs, but code is getting duplicated pretty quickly. I hope a custom marsheller will help me address the problem more easily.

And shorten SafeMatrixHandle.HasEqualHandle.
@ghost ghost closed this Jan 23, 2022
@ghost
Copy link

ghost commented Jan 23, 2022

Draft Pull Request was automatically closed for inactivity. Please let us know if you'd like to reopen it.

@teo-tsirpanis
Copy link
Contributor Author

Fair enough, I will open a new one once it is ready.

@ghost ghost locked as resolved and limited conversation to collaborators Feb 23, 2022
This pull request was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Drawing community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants