Skip to content

Commit 08a435c

Browse files
committed
Final updates to NativeClipboard
1 parent 8d8ffa4 commit 08a435c

File tree

1 file changed

+52
-9
lines changed

1 file changed

+52
-9
lines changed

Windows.Shell.Common/NativeClipboard.cs

+52-9
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,44 @@ public enum TextDataFormat
3434
}
3535

3636
/// <summary>
37-
/// Initializes and closes a session using the Clipboard calling <see cref="OpenClipboard"/> and then <see cref="CloseClipboard"/> on
38-
/// disposal. This can be called multiple times in nested calls and will ensure the Clipboard is only opened and closed at the highest scope.
37+
/// Static class with methods to interact with the Clipboard. This implementation relies exclusively on COM clipboard methods and does not use those from USER32.
3938
/// </summary>
40-
/// <seealso cref="System.IDisposable"/>
39+
/// <example>
40+
/// Below are two examples of a direct and indirect way to manipulate the clipboard.
41+
/// <code title="Using the NativeClipboard class to set single items.">// Set/get simple text
42+
/// NativeClipboard.SetText(txt, Vanara.Windows.Shell.TextDataFormat.UnicodeText);
43+
/// string getText = NativeClipboard.GetText(Vanara.Windows.Shell.TextDataFormat.UnicodeText);
44+
///
45+
/// // Set/get format as text
46+
/// NativeClipboard.SetData(Shell32.ShellClipboardFormat.CFSTR_FILENAMEW, @"C:\file1.txt");
47+
/// string getFile = (string)NativeClipboard.GetData(Shell32.ShellClipboardFormat.CFSTR_FILENAMEW);
48+
///
49+
/// // Set/get text, html and rtf formats
50+
/// NativeClipboard.SetText("Test", htmlFragment, rtfText); // sets text, html and rtf formats
51+
/// string html = NativeClipboard.GetText(Vanara.Windows.Shell.TextDataFormat.Html);
52+
///
53+
/// // Set/get text, url and html formats for a url
54+
/// NativeClipboard.SetUrl("https://microsoft.com", "Microsoft Home"); // sets text, url and html formats
55+
/// string url = (string)NativeClipboard.GetData(Shell32.ShellClipboardFormat.CFSTR_INETURLW);
56+
///
57+
/// // Set/get string arrays
58+
/// NativeClipboard.SetData(CLIPFORMAT.CF_HDROP, new[] { @"C:\file1.txt", @"C:\file2.txt" });
59+
/// string[] getFiles = (string[])NativeClipboard.GetData(CLIPFORMAT.CF_HDROP);
60+
///
61+
/// // Set/get structures
62+
/// NativeClipboard.SetData("MyRect", new RECT(1, 2, 3, 4));
63+
/// var rect = NativeClipboard.GetData&lt;RECT&gt;("MyRect");
64+
///
65+
/// // Set/get shell items
66+
/// NativeClipboard.SetShellItems(new[] { @"C:\file1.txt", @"C:\file2.txt" }.Select(ShellItem.Open));
67+
/// ShellItemArray getArray = NativeClipboard.GetShellItemArray();</code>
68+
/// <code title="Indirect manipulation">// This model let's you place multiple formats at once on the clipboard
69+
/// IDataObject ido = NativeClipboard.CreateEmptyDataObject();
70+
/// ido.SetData(CLIPFORMAT.CF_UNICODETEXT, txt);
71+
/// ido.SetData(Shell32.ShellClipboardFormat.CF_HTML, htmlFragment);
72+
/// ido.SetData("MyRectFormat", new RECT(1, 2, 3, 4));
73+
/// NativeClipboard.SetDataObject(ido);</code></example>
74+
/// <seealso cref="System.IDisposable" />
4175
public static class NativeClipboard
4276
{
4377
private const int stdRetryCnt = 5;
@@ -46,7 +80,6 @@ public static class NativeClipboard
4680
private static ListenerWindow listener;
4781
[ThreadStatic]
4882
private static bool oleInit = false;
49-
private static IComDataObject writableDataObj;
5083

5184
/// <summary>Occurs when whenever the contents of the Clipboard have changed.</summary>
5285
public static event EventHandler ClipboardUpdate
@@ -119,27 +152,29 @@ static IComDataObject ReadOnlyDataObject
119152
}
120153
}
121154

122-
/// <summary>Gets the writable data object.</summary>
123-
/// <value>The writable data object.</value>
124155
static IComDataObject WritableDataObj
125156
{
126157
get
127158
{
128-
if (writableDataObj is null)
129-
SHCreateDataObject(ppv: out writableDataObj).ThrowIfFailed();
159+
SHCreateDataObject(ppv: out var writableDataObj).ThrowIfFailed();
130160
return writableDataObj;
131161
}
132162
set
133163
{
134164
Init();
135165
TryMultThenThrowIfFailed(OleSetClipboard, value);
166+
Marshal.ReleaseComObject(value);
136167
Flush();
137168
}
138169
}
139170

140171
/// <summary>Clears the clipboard of any data or formatting.</summary>
141172
public static void Clear() => WritableDataObj = null;
142173

174+
/// <summary>Creates an empty, writable data object.</summary>
175+
/// <value>The data object.</value>
176+
public static IComDataObject CreateEmptyDataObject() => WritableDataObj;
177+
143178
/// <summary>Enumerates the data formats currently available on the clipboard.</summary>
144179
/// <returns>An enumeration of the data formats currently available on the clipboard.</returns>
145180
/// <remarks>
@@ -157,7 +192,7 @@ static IComDataObject WritableDataObj
157192
public static IEnumerable<uint> EnumAvailableFormats() => ReadOnlyDataObject.EnumFormats().Select(f => unchecked((uint)f.cfFormat));
158193

159194
/// <summary>Carries out the clipboard shutdown sequence. It also releases any IDataObject instances that were placed on the clipboard.</summary>
160-
public static void Flush() { Init(); TryMultThenThrowIfFailed(OleFlushClipboard); writableDataObj = null; }
195+
public static void Flush() { Init(); TryMultThenThrowIfFailed(OleFlushClipboard); }
161196

162197
/// <summary>Retrieves the window handle of the current owner of the clipboard.</summary>
163198
/// <returns>
@@ -474,6 +509,14 @@ public static void SetData(uint formatId, IEnumerable<string> values, StringList
474509
public static void SetData(string format, IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated, CharSet charSet = CharSet.Auto) =>
475510
SetData(RegisterFormat(format), values, packing, charSet);
476511

512+
/// <summary>
513+
/// Places a specific data object onto the clipboard. This makes the data object accessible to the OleGetClipboard function.
514+
/// </summary>
515+
/// <param name="dataObj">
516+
/// The IDataObject interface on the data object from which the data to be placed on the clipboard can be obtained.
517+
/// </param>
518+
public static void SetDataObject(IComDataObject dataObj) => WritableDataObj = dataObj ?? throw new ArgumentNullException(nameof(dataObj));
519+
477520
/// <summary>Puts a list of shell items onto the clipboard.</summary>
478521
/// <param name="shellItems">The sequence of shell items. The PIDL of each shell item must be absolute.</param>
479522
public static void SetShellItems(IEnumerable<ShellItem> shellItems) => WritableDataObj = (shellItems is ShellItemArray shia ? shia : new ShellItemArray(shellItems)).ToDataObject();

0 commit comments

Comments
 (0)