Skip to content

Refactor existing Clipboard code #10495

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

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

// Description: Helper methods for code that uses types from System.Drawing.

Expand All @@ -16,61 +17,30 @@
namespace MS.Internal
{
//FxCop can't tell that this class is instantiated via reflection, so suppress the FxCop warning.
[SuppressMessage("Microsoft.Performance","CA1812:AvoidUninstantiatedInternalClasses")]
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
internal class SystemDrawingExtension : SystemDrawingExtensionMethods
{
// return true if the data is a bitmap
internal override bool IsBitmap(object data)
{
return data is Bitmap;
}
internal override bool IsBitmap(object? data) => data is Bitmap;

// return true if the data is an Image
internal override bool IsImage(object data)
{
return data is Image;
}
internal override bool IsImage(object? data) => data is Image;

// return true if the data is a graphics metafile
internal override bool IsMetafile(object data)
{
return data is Metafile;
}
internal override bool IsMetafile(object? data) => data is Metafile;

// return the handle from a metafile
internal override IntPtr GetHandleFromMetafile(Object data)
internal override nint GetHandleFromMetafile(object? data) => data switch
{
IntPtr hMetafile = IntPtr.Zero;
Metafile metafile = data as Metafile;

if (metafile != null)
{
// Get the Windows handle from the metafile object.
hMetafile = metafile.GetHenhmetafile();
}
Metafile metafile => metafile.GetHenhmetafile(),
_ => 0
};

return hMetafile;
}
internal override object GetMetafileFromHemf(nint hMetafile) => new Metafile(hMetafile, deleteEmf: false);

// Get the metafile from the handle of the enhanced metafile.
internal override Object GetMetafileFromHemf(IntPtr hMetafile)
{
return new Metafile(hMetafile, false);
}

// Get a bitmap from the given data (either BitmapSource or Bitmap)
internal override object GetBitmap(object data)
{
return GetBitmapImpl(data);
}
internal override object? GetBitmap(object? data) => GetBitmapImpl(data);

// Get a bitmap handle from the given data (either BitmapSource or Bitmap)
// Also return its width and height.
internal override IntPtr GetHBitmap(object data, out int width, out int height)
internal override nint GetHBitmap(object? data, out int width, out int height)
{
Bitmap bitmapData = GetBitmapImpl(data);
Bitmap? bitmapData = GetBitmapImpl(data);

if (bitmapData == null)
if (bitmapData is null)
{
width = height = 0;
return IntPtr.Zero;
Expand All @@ -84,54 +54,45 @@ internal override IntPtr GetHBitmap(object data, out int width, out int height)
return bitmapData.GetHbitmap();
}

// Get a bitmap handle from a Bitmap
internal override IntPtr GetHBitmapFromBitmap(object data)
{
Bitmap bitmap = data as Bitmap;
return (bitmap != null) ? bitmap.GetHbitmap() : IntPtr.Zero;
}
internal override nint GetHBitmapFromBitmap(object? data) => data is Bitmap bitmap ? bitmap.GetHbitmap() : 0;

// Convert a metafile to HBitmap
internal override IntPtr ConvertMetafileToHBitmap(IntPtr handle)
internal override nint ConvertMetafileToHBitmap(nint handle)
{
Metafile metafile = new Metafile(handle, false);
Metafile metafile = new(handle, deleteEmf: false);

// Initialize the bitmap size to render the metafile.
int bitmapheight = metafile.Size.Height;
int bitmapwidth = metafile.Size.Width;
int bitmapwidth = metafile.Size.Width;

// We use System.Drawing to render metafile into the bitmap.
Bitmap bmp = new Bitmap(bitmapwidth, bitmapheight);
Graphics graphics = Graphics.FromImage(bmp);
// graphics.FillRectangle(new System.Drawing.SolidBrush(System.Drawing.Color.White), 0, 0, bitmapwidth, bitmapheight);
graphics.DrawImage(metafile, 0, 0, bitmapwidth, bitmapheight);

return bmp.GetHbitmap();
}

// return a stream for the ExifUserComment in the given Gif
internal override Stream GetCommentFromGifStream(Stream stream)
{
// Read the GIF header ...
Bitmap img = new Bitmap(stream);
// Read the comment as that is where the ISF is stored...
// for reference the tag is PropertyTagExifUserComment [0x9286] or 37510 (int)
PropertyItem piComment = img.GetPropertyItem(37510);
return new MemoryStream(piComment.Value);
// Read the GIF header
Bitmap bitmap = new(stream);

// Read the comment as that is where the ISF is stored.
// For reference the tag is PropertyTagExifUserComment [0x9286] or 37510 (int)
PropertyItem? piComment = bitmap.GetPropertyItem(37510);
return new MemoryStream(piComment!.Value!);
Copy link
Member

Choose a reason for hiding this comment

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

I think I'd expect a comment explaining the suppressions given that GetPropertyItem can actually return null.

Copy link
Member Author

Choose a reason for hiding this comment

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

This can totally throw. The answers here aren't great as modifying the throw is more likely to create grief than to be useful. We can put a comment in that says letting it throw as it would have for compat.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is not public, we can make it return null. The caller already catches NullReferenceException which it will cause.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll catch it in the other PR @miloush. I'm closing this one out in favor of the other after chatting with @harshit7962

}

// write a metafile stream to the output stream in PNG format
internal override void SaveMetafileToImageStream(MemoryStream metafileStream, Stream imageStream)
{
Metafile metafile = new Metafile(metafileStream);
Metafile metafile = new(metafileStream);
metafile.Save(imageStream, ImageFormat.Png);
}

// Get a bitmap from the given data (either BitmapSource or Bitmap)
private static Bitmap GetBitmapImpl(object data)
private static Bitmap? GetBitmapImpl(object? data)
{
BitmapSource bitmapSource = data as BitmapSource;
if (bitmapSource != null)
if (data is BitmapSource bitmapSource)
{
// Convert BitmapSource to System.Drawing.Bitmap to get Win32 HBITMAP.
BitmapEncoder bitmapEncoder;
Expand All @@ -152,23 +113,18 @@ private static Bitmap GetBitmapImpl(object data)
}
}

//returns bitmap snapshot of selected area
//this code takes a BitmapImage and converts it to a Bitmap so it can be put on the clipboard
internal override object GetBitmapFromBitmapSource(object source)
{
BitmapSource contentImage = (BitmapSource)source;
int imageWidth = (int)contentImage.Width;
int imageHeight = (int)contentImage.Height;

Bitmap bitmapFinal = new Bitmap(
imageWidth,
imageHeight,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
Bitmap bitmapFinal = new(imageWidth, imageHeight, PixelFormat.Format32bppRgb);

BitmapData bmData = bitmapFinal.LockBits(
new Rectangle(0, 0, imageWidth, imageHeight),
ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
new Rectangle(0, 0, imageWidth, imageHeight),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppRgb);

FormatConvertedBitmap formatConverter = new FormatConvertedBitmap();
formatConverter.BeginInit();
Expand All @@ -177,10 +133,10 @@ internal override object GetBitmapFromBitmapSource(object source)
formatConverter.EndInit();

formatConverter.CopyPixels(
new Int32Rect(0, 0, imageWidth, imageHeight),
bmData.Scan0,
bmData.Stride * (bmData.Height - 1) + (bmData.Width * 4),
bmData.Stride);
new Int32Rect(0, 0, imageWidth, imageHeight),
bmData.Scan0,
bmData.Stride * (bmData.Height - 1) + (bmData.Width * 4),
bmData.Stride);

bitmapFinal.UnlockBits(bmData);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataFormats.WaveAudio")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataFormats.Xaml")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataFormats.XamlPackage")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataObject.CopyingEvent")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataObject.PastingEvent")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Public API", Scope = "member", Target = "~F:System.Windows.DataObject.SettingDataEvent")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Clipboard.IsCurrent(System.Windows.IDataObject)~System.Boolean")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.DataObject.System#Runtime#InteropServices#ComTypes#IDataObject#EnumFormatEtc(System.Runtime.InteropServices.ComTypes.DATADIR)~System.Runtime.InteropServices.ComTypes.IEnumFORMATETC")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.OleServicesContext.SetDispatcherThread")]
Original file line number Diff line number Diff line change
@@ -1,108 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//
//
#nullable enable

// Description: Helper methods for code that uses types from System.Drawing.
//

using System.IO;
using Windows.Win32.Graphics.Gdi;

namespace MS.Internal
{
internal static class SystemDrawingHelper
{
// return true if the data is a bitmap
internal static bool IsBitmap(object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing();
return (extensions != null) ? extensions.IsBitmap(data) : false;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.IsBitmap(object?)"/>
internal static bool IsBitmap(object? data) =>
AssemblyHelper.ExtensionsForSystemDrawing()?.IsBitmap(data) ?? false;
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the benefit of these changes? Only makes it more difficult to debug to not have the extensions as explicit step and it gets optimized away anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

More class context on the screen at the same time. The impact to debugging doesn't seem to outweigh that benefit for something this simple.


// return true if the data is an Image
internal static bool IsImage(object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing();
return (extensions != null) ? extensions.IsImage(data) : false;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.IsImage(object?)"/>
internal static bool IsImage(object? data) =>
AssemblyHelper.ExtensionsForSystemDrawing()?.IsImage(data) ?? false;

// return true if the data is a graphics metafile
internal static bool IsMetafile(object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing();
return (extensions != null) ? extensions.IsMetafile(data) : false;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.IsMetafile(object?)"/>
internal static bool IsMetafile(object? data) =>
AssemblyHelper.ExtensionsForSystemDrawing()?.IsMetafile(data) ?? false;

// return the handle from a metafile
internal static IntPtr GetHandleFromMetafile(Object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing();
return (extensions != null) ? extensions.GetHandleFromMetafile(data) : IntPtr.Zero;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetHandleFromMetafile(object?)"/>
internal static HENHMETAFILE GetHandleFromMetafile(object? data) =>
(HENHMETAFILE)(AssemblyHelper.ExtensionsForSystemDrawing()?.GetHandleFromMetafile(data) ?? 0);

// Get the metafile from the handle of the enhanced metafile.
internal static Object GetMetafileFromHemf(IntPtr hMetafile)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
return extensions?.GetMetafileFromHemf(hMetafile);
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetMetafileFromHemf(nint)"/>
internal static object? GetMetafileFromHemf(HENHMETAFILE hMetafile) =>
AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.GetMetafileFromHemf(hMetafile);

// Get a bitmap from the given data (either BitmapSource or Bitmap)
internal static object GetBitmap(object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
return extensions?.GetBitmap(data);
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetBitmap(object?)"/>
internal static object? GetBitmap(object? data) =>
AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.GetBitmap(data);

// Get a bitmap handle from the given data (either BitmapSource or Bitmap)
// Also return its width and height.
internal static IntPtr GetHBitmap(object data, out int width, out int height)
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetHBitmap(object?, out int, out int)"/>
internal static HBITMAP GetHBitmap(object? data, out int width, out int height)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
if (extensions != null)
var extensions = AssemblyHelper.ExtensionsForSystemDrawing(force: true);
if (extensions is not null)
{
return extensions.GetHBitmap(data, out width, out height);
return (HBITMAP)extensions.GetHBitmap(data, out width, out height);
}

width = height = 0;
return IntPtr.Zero;
return HBITMAP.Null;
}

// Get a bitmap handle from a Bitmap
internal static IntPtr GetHBitmapFromBitmap(object data)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing();
return (extensions != null) ? extensions.GetHBitmapFromBitmap(data) : IntPtr.Zero;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetHBitmapFromBitmap(object?)"/>
internal static HBITMAP GetHBitmapFromBitmap(object? data) =>
(HBITMAP)(AssemblyHelper.ExtensionsForSystemDrawing()?.GetHBitmapFromBitmap(data) ?? HBITMAP.Null);

// Convert a metafile to HBitmap
internal static IntPtr ConvertMetafileToHBitmap(IntPtr handle)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
return (extensions != null) ? extensions.ConvertMetafileToHBitmap(handle) : IntPtr.Zero;
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.ConvertMetafileToHBitmap(nint)"/>
internal static HBITMAP ConvertMetafileToHBitmap(HENHMETAFILE handle) =>
(HBITMAP)(AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.ConvertMetafileToHBitmap(handle) ?? HBITMAP.Null);

// return a stream for the ExifUserComment in the given Gif
internal static Stream GetCommentFromGifStream(Stream stream)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
return extensions?.GetCommentFromGifStream(stream);
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetCommentFromGifStream(Stream)"/>
internal static Stream? GetCommentFromGifStream(Stream stream) =>
AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.GetCommentFromGifStream(stream);

// write a metafile stream to the output stream in PNG format
internal static void SaveMetafileToImageStream(MemoryStream metafileStream, Stream imageStream)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
extensions?.SaveMetafileToImageStream(metafileStream, imageStream);
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.SaveMetafileToImageStream(MemoryStream, Stream)"/>
internal static void SaveMetafileToImageStream(MemoryStream metafileStream, Stream imageStream) =>
AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.SaveMetafileToImageStream(metafileStream, imageStream);

//returns bitmap snapshot of selected area
//this code takes a BitmapImage and converts it to a Bitmap so it can be put on the clipboard
internal static object GetBitmapFromBitmapSource(object source)
{
SystemDrawingExtensionMethods extensions = AssemblyHelper.ExtensionsForSystemDrawing(force:true);
return extensions?.GetBitmapFromBitmapSource(source);
}
/// <inheritdoc cref="SystemDrawingExtensionMethods.GetBitmapFromBitmapSource(object)"/>
internal static object? GetBitmapFromBitmapSource(object source) =>
AssemblyHelper.ExtensionsForSystemDrawing(force: true)?.GetBitmapFromBitmapSource(source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@
<Compile Include="System\IO\Packaging\PackWebRequest.cs" />
<Compile Include="System\IO\Packaging\PackWebRequestFactory.cs" />
<Compile Include="System\IO\Packaging\PackWebResponse.cs" />
<Compile Include="System\Windows\DataObject.DataStore.cs" />
<Compile Include="System\Windows\DataObject.DataStore.DataStoreEntry.cs" />
<Compile Include="System\Windows\DataObject.FormatEnumerator.cs" />
<Compile Include="System\Windows\DataObject.OleConverter.cs" />
<Compile Include="System\Windows\DataObject.OleConverter.RestrictedTypeDeserializationException.cs" />
<Compile Include="System\Windows\DataObject.OleConverter.TypeRestrictingSerializationBinder.cs" />
<Compile Include="System\Windows\Generated\ContentElement.cs" />
<Compile Include="System\Windows\Generated\TextDecoration.cs" />
<Compile Include="System\Windows\Generated\TextDecorationCollection.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

namespace System.Windows;

public sealed partial class DataObject
{
private partial class DataStore
{
private class DataStoreEntry
{
public DataStoreEntry(object? data, bool autoConvert)
{
Data = data;
AutoConvert = autoConvert;
}

// Data object property.
public object? Data { get; set; }

// Auto convert proeprty.
public bool AutoConvert { get; }
}
}
}
Loading