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

[API Implementation]: Expose System.Drawing.Graphics.NativeGraphics. #63058

Closed
wants to merge 30 commits into from

Conversation

deeprobin
Copy link
Contributor

@deeprobin deeprobin commented Dec 21, 2021

Proposal implementation of dotnet/winforms#8833 (closes dotnet/winforms#8833)

Proposal

namespace System.Drawing
{
    public partial class Graphics
    {
        public IntPtr Handle { get; }
        public static Graphics FromHandle(IntPtr handle);
    }
    public partial class Brush
    {
        public IntPtr Handle { get; }
        public static Brush FromHandle(IntPtr handle);
    }
    public partial class Pen
    {
        public IntPtr Handle { get; }
        public static Pen FromHandle(IntPtr handle);
    }
    public partial class Image
    {
        public IntPtr Handle { get; }
        public static Image FromHandle(IntPtr handle);
    }

    public partial class Region
    {
        public IntPtr Handle { get; }
        public static Region FromHandle(IntPtr handle);
    }
}
namespace System.Drawing.Drawing2D
{
    public partial class Matrix
    {
        public IntPtr Handle { get; }
        public static Matrix FromHandle(IntPtr handle);
    }

    public partial class GraphicsPath
    {
        public IntPtr Handle { get; }
        public static GraphicsPath FromHandle(IntPtr handle);
    }
}

Current state of implementation

  • Implementation
  • Ref Assembly
  • Documentation comments
  • Fix project files
  • Tests

/cc @danmoseley

@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Dec 21, 2021
@ghost
Copy link

ghost commented Dec 21, 2021

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

Issue Details

Proposal implementation of dotnet/winforms#8833 (closes dotnet/winforms#8833)

Proposal

namespace System.Drawing
{
    public partial class Graphics
    {
        public IntPtr Handle { get; }
        public static Graphics FromHandle(IntPtr handle);
    }
    public partial class Brush
    {
        public IntPtr Handle { get; }
        public static Brush FromHandle(IntPtr handle);
    }
    public partial class Pen
    {
        public IntPtr Handle { get; }
        public static Pen FromHandle(IntPtr handle);
    }
    public partial class Image
    {
        public IntPtr Handle { get; }
        public static Image FromHandle(IntPtr handle);
    }

    public partial class Region
    {
        public IntPtr Handle { get; }
        public static Region FromHandle(IntPtr handle);
    }
}
namespace System.Drawing.Drawing2D
{
    public partial class Matrix
    {
        public IntPtr Handle { get; }
        public static Matrix FromHandle(IntPtr handle);
    }

    public partial class GraphicsPath
    {
        public IntPtr Handle { get; }
        public static GraphicsPath FromHandle(IntPtr handle);
    }
}

Current state of implementation

  • Implementation
  • Ref Assembly
  • Documentation comments
  • Fix project files
  • I think because we forward in most cases the method to the private constructor, that we need no tests

/cc @danmoseley

Author: deeprobin
Assignees: -
Labels:

area-System.Drawing, new-api-needs-documentation

Milestone: -

@danmoseley
Copy link
Member

I think because we forward in most cases the method to the private constructor, that we need no tests

We do (or should) test all public API even pass throughs - the code will exist for years, refactoring happens over time and can introduce some issue or another. Plus, it ekes out a little more code coverage when we scan for missing coverage.

@deeprobin
Copy link
Contributor Author

@danmoseley Why is there still a build pipeline for NET Framework (which fails)? I thought that is officially not supported anymore?

@teo-tsirpanis
Copy link
Contributor

@deeprobin, System.Drawing.Common tests are executed on both .NET Framework and modern .NET to ensure behavioral compatibility between the two editions of the same library.

@danmoseley
Copy link
Member

@deeprobin how's this one going? do you need help?

You have removed some entries from the ref, which creates a breaking change for code building against the non-netcoreapp target framework, eg., for .NET Standard. Existing API should stay in there. New API can go in the ref/...netcoreapp.cs file.

If possible, please use https://github.com/dotnet/runtime/blob/4679edc6be3eb91263a7acfe542dfd30b53ed2bf/docs/coding-guidelines/updating-ref-source.md#for-most-assemblies-within-libraries -- it doesn't understand multiple ref files, but you can copy/paste the result to try to get formatting like the tool would create.

@deeprobin
Copy link
Contributor Author

deeprobin commented Jan 9, 2022

@deeprobin how's this one going? do you need help?

You have removed some entries from the ref, which creates a breaking change for code building against the non-netcoreapp target framework, eg., for .NET Standard. Existing API should stay in there. New API can go in the ref/...netcoreapp.cs file.

If possible, please use https://github.com/dotnet/runtime/blob/4679edc6be3eb91263a7acfe542dfd30b53ed2bf/docs/coding-guidelines/updating-ref-source.md#for-most-assemblies-within-libraries -- it doesn't understand multiple ref files, but you can copy/paste the result to try to get formatting like the tool would create.

@danmoseley

Oh yes, I think I fixed that.

CI is now failing: exit code 139 means SIGSEGV Illegal memory access. Deref invalid pointer, overrunning buffer, stack overflow etc. Core dumped. (because the test-pointer does not exist)

How would you construct a test for this?

Creating 2 Handle-objects and swapping the Handle property? Or do you have a better idea to test this?

@danmoseley
Copy link
Member

Not sure of your question. If you're asking - how would we construct a test that expected a fatal error like a segfault? - we do not test for fatal errors, except possibly in some exceptional cases (and then by using RemoteExecutor.Invoke).

@deeprobin
Copy link
Contributor Author

deeprobin commented Jan 10, 2022

@danmoseley I have looked at the tests again. The CI throws gdiplus errors.
Do you think this is a case of #22221.

https://helixre8s23ayyeko0k025g8.blob.core.windows.net/dotnet-runtime-refs-pull-63058-merge-ffd8398ff9224cd9a3/System.Drawing.Common.Tests/1/console.340b41fa.log?sv=2019-07-07&se=2022-01-29T01%3A02%3A29Z&sr=c&sp=rl&sig=2gsJHhJexH7K8z5Agh30IsXzPwEOXfezeP2vnLL0oNM%3D

      System.TypeInitializationException : The type initializer for 'Gdip' threw an exception.
      ---- System.DllNotFoundException : Unable to load shared library 'libgdiplus' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: Error loading shared library liblibgdiplus: No such file or directory
      Stack Trace:
    System.Drawing.Drawing2D.Tests.MatrixTests.FromHandle [FAIL]
        /_/src/libraries/System.Drawing.Common/src/Microsoft.Interop.DllImportGenerator/Microsoft.Interop.DllImportGenerator/GeneratedDllImports.g.cs(518,0): at System.Drawing.SafeNativeMethods.Gdip.GdipCreateMatrix(IntPtr& matrix)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs(18,0): at System.Drawing.Drawing2D.Matrix..ctor()
        /_/src/libraries/System.Drawing.Common/tests/Drawing2D/MatrixTests.Core.cs(47,0): at System.Drawing.Drawing2D.Tests.MatrixTests.FromHandle()
        ----- Inner Stack Trace -----
           at System.Drawing.SafeNativeMethods.Gdip.GdiplusStartup(IntPtr& token, StartupInput& input, StartupOutput& output)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Gdiplus.cs(33,0): at System.Drawing.SafeNativeMethods.Gdip..cctor()

This seems to be similar #22221 (comment) to this run https://helixre8s23ayyeko0k025g8.blob.core.windows.net/dotnet-runtime-refs-pull-63058-merge-14bfe203df1a49fb92/System.Drawing.Common.Tests/1/console.8315be5d.log?sv=2019-07-07&se=2022-01-29T01%3A01%3A45Z&sr=c&sp=rl&sig=4Q5rnTbBhb3SPBRrdJgUCw9sB3Ep9GaNa6i17znSJX8%3D

=================================================================
	Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x7f1e9103ea6d):0x7f1e9103ea5d  c0 0f 85 ac 02 00 00 48 85 ff 0f 84 23 01 00 00  .......H....#...
0x7f1e9103ea6d  48 8b 77 f8 4c 8d 6f f0 40 f6 c6 02 0f 85 39 01  [email protected].
0x7f1e9103ea7d  00 00 48 8b 2d f2 32 35 00 64 48 83 7d 00 00 0f  ..H.-.25.dH.}...
0x7f1e9103ea8d  84 ee 04 00 00 40 f6 c6 04 48 8d 1d a3 41 35 00  [email protected].

=================================================================
	Managed Stacktrace:
=================================================================
	  at <unknown> <0xffffffff>
	  at Gdip:GdipDeletePen <0x000c4>
	  at System.Drawing.Pen:Dispose <0x0014b>
	  at System.Drawing.Pen:Finalize <0x0002f>
	  at System.Object:runtime_invoke_virtual_void__this__ <0x0008c>
=================================================================

@deeprobin
Copy link
Contributor Author

@qmfrederik @eerhardt Can you perhaps give some thoughts on this, since you were involved in #22221?

@deeprobin
Copy link
Contributor Author

I have already implemented the change with the ActiveIssueAttribute. In case of doubt we can also revert this.

https://github.com/deeprobin/runtime/tree/f39d2a3aeef9da389e9de5da7c77ad798e6461f1

@danmoseley
Copy link
Member

@deeprobin your tests are causing a double-dispose, eg:

        public void FromHandle()
        {
            var region = new Region();

            var expectedHandle = region.Handle;
            var actualRegion = Region.FromHandle(expectedHandle);

            IntPtr actualHandle = actualRegion.Handle;
            Assert.Equal(expectedHandle, actualHandle);

            // Do not dispose `region` because it is the same handle
            actualRegion.Dispose();
        }

Here you're disposing actualRegion which is calling GdipDeleteRegion with this handle. But the Region object is IDisposable - so when it becomes unreachable and is collected, the GC will put it on the finalizer queue. And the finalizer will call dispose, which will call GdipDeleteRegion on the same handle. You see in the log that GDI+ is returning 4, which means ObjectBusy. I am guessing that GdipDeleteRegion may be safe to call twice on the same handle, but not concurrently, which is likely what's happening here.

The fix is likely to change var region = new Region(); to using var region = new Region();. This will cause the Dispose to happen deterministically. The same issue applies to the other tests.

Because you've got 8 PR's active at this time, I'm going to close this one. Please reactivate when you believe all the tests should be passing again. Thanks.

@danmoseley danmoseley closed this Jan 18, 2022
public System.IntPtr Handle { get { throw null; } }
public static System.Drawing.Graphics FromHandle(System.IntPtr handle) { throw null; }
}

Copy link
Member

Choose a reason for hiding this comment

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

Our goal is to either generate these files (as discussed earlier) or make it look like the generator created it. The generator won't insert blank lines like this as we would in normal code.

/// <summary>
/// The underlying graphics path handle
/// </summary>
public IntPtr Handle => _nativePath;
Copy link
Member

Choose a reason for hiding this comment

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

We try to order fields then properties then methods, with public before private. I'm surprised the editorconfig isn't flagging this for you.

@ghost ghost locked as resolved and limited conversation to collaborators Feb 18, 2022
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 new-api-needs-documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API Proposal: Expose System.Drawing.Graphics.NativeGraphics.
3 participants