dotnet / dotnet/winforms

API Proposal: Expose `System.Drawing.Graphics.NativeGraphics`.

Open
#8,833 29 comments 5 reactions 1 assignee Assigned to @JeremyKuhne View on GitHub
api-approved area-Interop area-System.Drawing tenet-performance
Dominant language
C#
Stars
4.9k
Forks
1.1k
Avg merge
1d 13m
Merged PRs (30d)
85

Description

## Background and Motivation

In Windows Forms painting it would be helpful if we had the option to plug our own P/Invokes calls into existing GDI+ objects. We could, for example, have lighter-weight `GpPen` and `GpBrush` wrappers (structs perhaps) and invoke the GDI+ API directly with them via the exposed `Graphics` pointer.

While we could create our own `GpGraphics` object that usually isn't practical as we often have to interop via the System.Drawing types.

The form of the API here follows what `Icon` does (which is a GDI wrapper). Having a static construction method is necessary as we sometimes have derived types that we want to return (`SolidBrush`, `TextureBrush`, etc.).

## Proposed API

```diff
namespace System.Drawing
{
public class Graphics
{
+ public IntPtr Handle { get; }
+ public static Graphics FromHandle(IntPtr handle);
}

public abstract class Brush
{
+ public IntPtr Handle { get; }
+ public static Brush FromHandle(IntPtr handle);
}

public class Pen
{
+ public IntPtr Handle { get; }
+ public static Pen FromHandle(IntPtr handle);
}

public class Image
{
+ public IntPtr Handle { get; }
+ public static Image FromHandle(IntPtr handle);
}

public class Region
{
+ public IntPtr Handle { get; }
+ public static Region FromHandle(IntPtr handle);
}
}

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

public class GraphicsPath
{
+ public IntPtr Handle { get; }
+ public static GraphicsPath FromHandle(IntPtr handle);
}
}
```
## Notes

- Like `Icon.FromHandle`, these `FromHandle` APIs will not take ownership and closing the handle is the caller's responsibility.

## Risks

No specific risks outside of what comes normally with accessing backing handles. You can delete the backing object or try to use the handle after the `Graphics` closes the native handle. Not much different than what you'd be running into using a disposed `Graphics`.

Note that we already expose `IDeviceContext` on `Graphics` which is much more risky as everything on `Graphics` throws when you're in-between `GetHDC()` and `ReleaseHDC()`. The throw there comes from GDI+ as it doesn't want to conflict with the usage of the backing HDC. GDI+ maintains and restores the state of the HDC outside of those calls.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.