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

Restructure DataObject #12792

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions src/System.Private.Windows.Core/src/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopDataObject.Composition.NativeToDesktopAdapter.ReadByteStreamFromHGLOBAL(Windows.Win32.Foundation.HGLOBAL,System.Boolean@)~System.IO.MemoryStream")]
Copy link
Member

Choose a reason for hiding this comment

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

We used the word Windows, not Desktop as a prefix to indicate applicability to both WPF and winforms in the AppContext switch name. Should these implementation classes follow this pattern? Or Core to make it shorter?

[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopDataObject.Composition.NativeToRuntimeAdapter.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.Private.Windows.Core.OLE.DesktopClipboard.SetDataObject(System.Object,System.Boolean,System.Int32,System.Int32)")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopClipboard.GetDataObject~System.Private.Windows.Core.OLE.IDataObjectDesktop")]
17 changes: 17 additions & 0 deletions src/System.Private.Windows.Core/src/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ BeginPaint
BI_COMPRESSION
BitBlt
BOOL
CFSTR_DROPDESCRIPTION
CFSTR_INDRAGLOOP
CLIPBRD_E_BAD_DATA
CLR_*
CallWindowProc
CoCreateInstance
CombineRgn
CopyImage
CP_ACP
CreateBitmap
CreateCompatibleBitmap
CreateCompatibleDC
Expand Down Expand Up @@ -50,7 +53,11 @@ DRAGDROP_S_CANCEL
DRAGDROP_S_DROP
DRAGDROP_S_USEDEFAULTCURSORS
DrawIconEx
DragQueryFile
DROPDESCRIPTION
DROPFILES
DV_E_*
DVASPECT
E_ABORT
E_ACCESSDENIED
E_FAIL
Expand Down Expand Up @@ -78,6 +85,7 @@ GetWindowText
GetWindowTextLength
GET_CLASS_LONG_INDEX
GetClientRect
GetClipboardFormatName
GetClipRgn
GetDC
GetDCEx
Expand Down Expand Up @@ -119,7 +127,9 @@ HPROPSHEETPAGE
HRGN
HWND
HWND_*
IDataObject
IDI_*
IDragSourceHelper2
IEnumUnknown
IGlobalInterfaceTable
ImageLockMode
Expand Down Expand Up @@ -162,6 +172,10 @@ OLE_E_INVALIDRECT
OLE_E_NOCONNECTION
OLE_E_PROMPTSAVECANCELLED
OleCreatePictureIndirect
OleDuplicateData
OleFlushClipboard
OleGetClipboard
OleSetClipboard
PeekMessage
PostMessage
POINTS
Expand All @@ -171,7 +185,9 @@ PWSTR
RealizePalette
RECT
REGDB_E_CLASSNOTREG
RegisterClipboardFormat
ReleaseDC
ReleaseStgMedium
RestoreDC
RPC_E_CHANGED_MODE
RPC_E_DISCONNECTED
Expand Down Expand Up @@ -215,6 +231,7 @@ SystemParametersInfoForDpi
TYPE_E_BADMODULEKIND
UNICODE_STRING_MAX_CHARS
VIEW_E_DRAW
WideCharToMultiByte
WIN32_ERROR
WINCODEC_ERR_*
WINDOW_LONG_PTR_INDEX
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Private.Windows.Core.OLE;

/// <summary>
/// Translates between WinForms text-based <see cref="DesktopClipboard"/>
/// formats and Win32 32-bit signed integer-based clipboard
/// formats. Provides <see langword="static"/> methods to create new
/// <see cref="DesktopClipboard"/> formats and add them to the Windows Registry.
/// </summary>
internal static partial class DesktopDataFormats
{
/// <summary>
/// Represents a format type.
/// </summary>
public class Format
{
/// <summary>
/// Initializes a new instance of the <see cref="Format"/> class and
/// specifies whether a Win32 handle is expected with this format.
/// </summary>
public Format(string name, int id)
{
Name = name;
Id = id;
}

/// <summary>
/// Specifies the name of this format.
/// </summary>
public string Name { get; }

/// <summary>
/// Specifies the ID number for this format.
/// </summary>
public int Id { get; }
}
}
193 changes: 193 additions & 0 deletions src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Private.Windows.Core.Resources;
using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.System.Ole;

namespace System.Private.Windows.Core.OLE;

/// <summary>
/// Translates between WinForms text-based <see cref="DesktopClipboard"/>
/// formats and Win32 32-bit signed integer-based clipboard
/// formats. Provides <see langword="static"/> methods to create new
/// <see cref="DesktopClipboard"/> formats and add them to the Windows Registry.
/// </summary>
internal static partial class DesktopDataFormats
{
internal const string TextConstant = "Text";
internal const string UnicodeTextConstant = "UnicodeText";
internal const string DibConstant = "DeviceIndependentBitmap";
internal const string BitmapConstant = "Bitmap";
internal const string EmfConstant = "EnhancedMetafile";
internal const string WmfConstant = "MetaFilePict";
internal const string SymbolicLinkConstant = "SymbolicLink";
internal const string DifConstant = "DataInterchangeFormat";
internal const string TiffConstant = "TaggedImageFileFormat";
internal const string OemTextConstant = "OEMText";
internal const string PaletteConstant = "Palette";
internal const string PenDataConstant = "PenData";
internal const string RiffConstant = "RiffAudio";
internal const string WaveAudioConstant = "WaveAudio";
internal const string FileDropConstant = "FileDrop";
internal const string LocaleConstant = "Locale";
internal const string HtmlConstant = "HTML Format";
internal const string RtfConstant = "Rich Text Format";
internal const string CsvConstant = "Csv";
internal const string StringConstant = "System.String";
internal const string SerializableConstant = "WindowsForms10PersistentObject";

private static Format[]? s_formatList;

private static int s_formatCount;

#if NET9_0_OR_GREATER
private static readonly Lock s_internalSyncObject = new();
#else
private static readonly object s_internalSyncObject = new();
#endif

/// <summary>
/// Gets a <see cref="Format"/> with the Windows Clipboard numeric ID and name for the specified format.
/// </summary>
public static Format GetFormat(string format)
{
ArgumentException.ThrowIfNullOrWhiteSpace(format);

lock (s_internalSyncObject)
{
EnsurePredefined();

// It is much faster to do a case sensitive search here.
// So do the case sensitive compare first, then the expensive one.
for (int n = 0; n < s_formatCount; n++)
{
if (s_formatList[n].Name.Equals(format))
{
return s_formatList[n];
}
}

for (int n = 0; n < s_formatCount; n++)
{
if (string.Equals(s_formatList[n].Name, format, StringComparison.OrdinalIgnoreCase))
{
return s_formatList[n];
}
}

// Need to add this format string
uint formatId = PInvokeCore.RegisterClipboardFormat(format);
if (formatId == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), SR.RegisterCFFailed);
}

EnsureFormatSpace(1);
s_formatList[s_formatCount] = new Format(format, (int)formatId);
return s_formatList[s_formatCount++];
}
}

/// <summary>
/// Gets a <see cref="Format"/> with the Windows Clipboard numeric ID and name for the specified ID.
/// </summary>
internal static unsafe Format GetFormat(ushort id)
{
lock (s_internalSyncObject)
{
EnsurePredefined();

for (int n = 0; n < s_formatCount; n++)
{
if (s_formatList[n].Id == id)
{
return s_formatList[n];
}
}

string? name = null;

// The max length of the name of clipboard formats is equal to the max length
// of a Win32 Atom of 255 chars. An additional null terminator character is added,
// giving a required capacity of 256 chars.
Span<char> formatName = stackalloc char[256];
fixed (char* pFormatName = formatName)
{
int length = PInvokeCore.GetClipboardFormatName(id, pFormatName, 256);
if (length != 0)
{
name = formatName[..length].ToString();
}
}

// This can happen if windows adds a standard format that we don't know about,
// so we should play it safe.
name ??= $"Format{id}";

EnsureFormatSpace(1);
s_formatList[s_formatCount] = new Format(name, id);
return s_formatList[s_formatCount++];
}
}

/// <summary>
/// Ensures that we have enough room in our format list
/// </summary>
[MemberNotNull(nameof(s_formatList))]
private static void EnsureFormatSpace(int size)
{
if (s_formatList is null || s_formatList.Length <= s_formatCount + size)
{
int newSize = s_formatCount + 20;

Format[] newList = new Format[newSize];
for (int n = 0; n < s_formatCount; n++)
{
newList[n] = s_formatList![n];
}

s_formatList = newList;
}
}

/// <summary>
/// Ensures that the Win32 predefined formats are setup in our format list.
/// This is called anytime we need to search the list
/// </summary>
[MemberNotNull(nameof(s_formatList))]
private static void EnsurePredefined()
{
if (s_formatCount == 0)
{
s_formatList =
[
// Text name Win32 format ID
new(UnicodeTextConstant, (int)CLIPBOARD_FORMAT.CF_UNICODETEXT),
new(TextConstant, (int)CLIPBOARD_FORMAT.CF_TEXT),
new(BitmapConstant, (int)CLIPBOARD_FORMAT.CF_BITMAP),
new(WmfConstant, (int)CLIPBOARD_FORMAT.CF_METAFILEPICT),
new(EmfConstant, (int)CLIPBOARD_FORMAT.CF_ENHMETAFILE),
new(DifConstant, (int)CLIPBOARD_FORMAT.CF_DIF),
new(TiffConstant, (int)CLIPBOARD_FORMAT.CF_TIFF),
new(OemTextConstant, (int)CLIPBOARD_FORMAT.CF_OEMTEXT),
new(DibConstant, (int)CLIPBOARD_FORMAT.CF_DIB),
new(PaletteConstant, (int)CLIPBOARD_FORMAT.CF_PALETTE),
new(PenDataConstant, (int)CLIPBOARD_FORMAT.CF_PENDATA),
new(RiffConstant, (int)CLIPBOARD_FORMAT.CF_RIFF),
new(WaveAudioConstant, (int)CLIPBOARD_FORMAT.CF_WAVE),
new(SymbolicLinkConstant, (int)CLIPBOARD_FORMAT.CF_SYLK),
new(FileDropConstant, (int)CLIPBOARD_FORMAT.CF_HDROP),
new(LocaleConstant, (int)CLIPBOARD_FORMAT.CF_LOCALE)
];

s_formatCount = s_formatList.Length;
}
else
{
s_formatList ??= [];
}
}
}
Loading
Loading