|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | + |
| 4 | +using System.ComponentModel; |
| 5 | +using System.Private.Windows.Core.Resources; |
| 6 | +using System.Runtime.InteropServices; |
| 7 | +using Windows.Win32; |
| 8 | +using Windows.Win32.System.Ole; |
| 9 | + |
| 10 | +namespace System.Private.Windows.Core.OLE; |
| 11 | + |
| 12 | +/// <summary> |
| 13 | +/// Translates between WinForms text-based <see cref="DesktopClipboard"/> |
| 14 | +/// formats and Win32 32-bit signed integer-based clipboard |
| 15 | +/// formats. Provides <see langword="static"/> methods to create new |
| 16 | +/// <see cref="DesktopClipboard"/> formats and add them to the Windows Registry. |
| 17 | +/// </summary> |
| 18 | +internal static partial class DesktopDataFormats |
| 19 | +{ |
| 20 | + internal const string TextConstant = "Text"; |
| 21 | + internal const string UnicodeTextConstant = "UnicodeText"; |
| 22 | + internal const string DibConstant = "DeviceIndependentBitmap"; |
| 23 | + internal const string BitmapConstant = "Bitmap"; |
| 24 | + internal const string EmfConstant = "EnhancedMetafile"; |
| 25 | + internal const string WmfConstant = "MetaFilePict"; |
| 26 | + internal const string SymbolicLinkConstant = "SymbolicLink"; |
| 27 | + internal const string DifConstant = "DataInterchangeFormat"; |
| 28 | + internal const string TiffConstant = "TaggedImageFileFormat"; |
| 29 | + internal const string OemTextConstant = "OEMText"; |
| 30 | + internal const string PaletteConstant = "Palette"; |
| 31 | + internal const string PenDataConstant = "PenData"; |
| 32 | + internal const string RiffConstant = "RiffAudio"; |
| 33 | + internal const string WaveAudioConstant = "WaveAudio"; |
| 34 | + internal const string FileDropConstant = "FileDrop"; |
| 35 | + internal const string LocaleConstant = "Locale"; |
| 36 | + internal const string HtmlConstant = "HTML Format"; |
| 37 | + internal const string RtfConstant = "Rich Text Format"; |
| 38 | + internal const string CsvConstant = "Csv"; |
| 39 | + internal const string StringConstant = "System.String"; |
| 40 | + internal const string SerializableConstant = "WindowsForms10PersistentObject"; |
| 41 | + |
| 42 | + private static Format[]? s_formatList; |
| 43 | + |
| 44 | + private static int s_formatCount; |
| 45 | + |
| 46 | +#if NET9_0_OR_GREATER |
| 47 | + private static readonly Lock s_internalSyncObject = new(); |
| 48 | +#else |
| 49 | + private static readonly object s_internalSyncObject = new(); |
| 50 | +#endif |
| 51 | + |
| 52 | + /// <summary> |
| 53 | + /// Gets a <see cref="Format"/> with the Windows Clipboard numeric ID and name for the specified format. |
| 54 | + /// </summary> |
| 55 | + public static Format GetFormat(string format) |
| 56 | + { |
| 57 | + ArgumentException.ThrowIfNullOrWhiteSpace(format); |
| 58 | + |
| 59 | + lock (s_internalSyncObject) |
| 60 | + { |
| 61 | + EnsurePredefined(); |
| 62 | + |
| 63 | + // It is much faster to do a case sensitive search here. |
| 64 | + // So do the case sensitive compare first, then the expensive one. |
| 65 | + for (int n = 0; n < s_formatCount; n++) |
| 66 | + { |
| 67 | + if (s_formatList[n].Name.Equals(format)) |
| 68 | + { |
| 69 | + return s_formatList[n]; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + for (int n = 0; n < s_formatCount; n++) |
| 74 | + { |
| 75 | + if (string.Equals(s_formatList[n].Name, format, StringComparison.OrdinalIgnoreCase)) |
| 76 | + { |
| 77 | + return s_formatList[n]; |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + // Need to add this format string |
| 82 | + uint formatId = PInvokeCore.RegisterClipboardFormat(format); |
| 83 | + if (formatId == 0) |
| 84 | + { |
| 85 | + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.RegisterCFFailed); |
| 86 | + } |
| 87 | + |
| 88 | + EnsureFormatSpace(1); |
| 89 | + s_formatList[s_formatCount] = new Format(format, (int)formatId); |
| 90 | + return s_formatList[s_formatCount++]; |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + /// <summary> |
| 95 | + /// Gets a <see cref="Format"/> with the Windows Clipboard numeric ID and name for the specified ID. |
| 96 | + /// </summary> |
| 97 | + internal static unsafe Format GetFormat(ushort id) |
| 98 | + { |
| 99 | + lock (s_internalSyncObject) |
| 100 | + { |
| 101 | + EnsurePredefined(); |
| 102 | + |
| 103 | + for (int n = 0; n < s_formatCount; n++) |
| 104 | + { |
| 105 | + if (s_formatList[n].Id == id) |
| 106 | + { |
| 107 | + return s_formatList[n]; |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + string? name = null; |
| 112 | + |
| 113 | + // The max length of the name of clipboard formats is equal to the max length |
| 114 | + // of a Win32 Atom of 255 chars. An additional null terminator character is added, |
| 115 | + // giving a required capacity of 256 chars. |
| 116 | + Span<char> formatName = stackalloc char[256]; |
| 117 | + fixed (char* pFormatName = formatName) |
| 118 | + { |
| 119 | + int length = PInvokeCore.GetClipboardFormatName(id, pFormatName, 256); |
| 120 | + if (length != 0) |
| 121 | + { |
| 122 | + name = formatName[..length].ToString(); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + // This can happen if windows adds a standard format that we don't know about, |
| 127 | + // so we should play it safe. |
| 128 | + name ??= $"Format{id}"; |
| 129 | + |
| 130 | + EnsureFormatSpace(1); |
| 131 | + s_formatList[s_formatCount] = new Format(name, id); |
| 132 | + return s_formatList[s_formatCount++]; |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + /// <summary> |
| 137 | + /// Ensures that we have enough room in our format list |
| 138 | + /// </summary> |
| 139 | + [MemberNotNull(nameof(s_formatList))] |
| 140 | + private static void EnsureFormatSpace(int size) |
| 141 | + { |
| 142 | + if (s_formatList is null || s_formatList.Length <= s_formatCount + size) |
| 143 | + { |
| 144 | + int newSize = s_formatCount + 20; |
| 145 | + |
| 146 | + Format[] newList = new Format[newSize]; |
| 147 | + for (int n = 0; n < s_formatCount; n++) |
| 148 | + { |
| 149 | + newList[n] = s_formatList![n]; |
| 150 | + } |
| 151 | + |
| 152 | + s_formatList = newList; |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + /// <summary> |
| 157 | + /// Ensures that the Win32 predefined formats are setup in our format list. |
| 158 | + /// This is called anytime we need to search the list |
| 159 | + /// </summary> |
| 160 | + [MemberNotNull(nameof(s_formatList))] |
| 161 | + private static void EnsurePredefined() |
| 162 | + { |
| 163 | + if (s_formatCount == 0) |
| 164 | + { |
| 165 | + s_formatList = |
| 166 | + [ |
| 167 | + // Text name Win32 format ID |
| 168 | + new(UnicodeTextConstant, (int)CLIPBOARD_FORMAT.CF_UNICODETEXT), |
| 169 | + new(TextConstant, (int)CLIPBOARD_FORMAT.CF_TEXT), |
| 170 | + new(BitmapConstant, (int)CLIPBOARD_FORMAT.CF_BITMAP), |
| 171 | + new(WmfConstant, (int)CLIPBOARD_FORMAT.CF_METAFILEPICT), |
| 172 | + new(EmfConstant, (int)CLIPBOARD_FORMAT.CF_ENHMETAFILE), |
| 173 | + new(DifConstant, (int)CLIPBOARD_FORMAT.CF_DIF), |
| 174 | + new(TiffConstant, (int)CLIPBOARD_FORMAT.CF_TIFF), |
| 175 | + new(OemTextConstant, (int)CLIPBOARD_FORMAT.CF_OEMTEXT), |
| 176 | + new(DibConstant, (int)CLIPBOARD_FORMAT.CF_DIB), |
| 177 | + new(PaletteConstant, (int)CLIPBOARD_FORMAT.CF_PALETTE), |
| 178 | + new(PenDataConstant, (int)CLIPBOARD_FORMAT.CF_PENDATA), |
| 179 | + new(RiffConstant, (int)CLIPBOARD_FORMAT.CF_RIFF), |
| 180 | + new(WaveAudioConstant, (int)CLIPBOARD_FORMAT.CF_WAVE), |
| 181 | + new(SymbolicLinkConstant, (int)CLIPBOARD_FORMAT.CF_SYLK), |
| 182 | + new(FileDropConstant, (int)CLIPBOARD_FORMAT.CF_HDROP), |
| 183 | + new(LocaleConstant, (int)CLIPBOARD_FORMAT.CF_LOCALE) |
| 184 | + ]; |
| 185 | + |
| 186 | + s_formatCount = s_formatList.Length; |
| 187 | + } |
| 188 | + else |
| 189 | + { |
| 190 | + s_formatList ??= []; |
| 191 | + } |
| 192 | + } |
| 193 | +} |
0 commit comments