@@ -43,6 +43,8 @@ public static class NativeClipboard
43
43
private const int stdRetryCnt = 5 ;
44
44
private static readonly object objectLock = new ( ) ;
45
45
private static ListenerWindow listener ;
46
+ [ ThreadStatic ]
47
+ private static bool oleInit = false ;
46
48
private static IComDataObject writableDataObj ;
47
49
48
50
/// <summary>Occurs when whenever the contents of the Clipboard have changed.</summary>
@@ -82,36 +84,26 @@ public static IEnumerable<uint> CurrentlySupportedFormats
82
84
}
83
85
}
84
86
85
- static bool TryMultThenThrowIfFailed ( Func < HRESULT > func , int n = stdRetryCnt )
86
- {
87
- HRESULT hr = HRESULT . S_OK ;
88
- for ( int i = 1 ; i <= n ; i ++ )
89
- {
90
- hr = func ( ) ;
91
- if ( hr . Failed && i == n )
92
- throw hr . GetException ( ) ;
93
- }
94
- return hr == HRESULT . S_OK ;
95
- }
96
-
97
- static bool TryMultThenThrowIfFailed ( Func < IComDataObject , HRESULT > func , IComDataObject o , int n = stdRetryCnt )
98
- {
99
- HRESULT hr = HRESULT . S_OK ;
100
- for ( int i = 1 ; i <= n ; i ++ )
101
- {
102
- hr = func ( o ) ;
103
- if ( hr . Failed && i == n )
104
- throw hr . GetException ( ) ;
105
- }
106
- return hr == HRESULT . S_OK ;
107
- }
87
+ /// <summary>Retrieves the clipboard sequence number for the current window station.</summary>
88
+ /// <returns>
89
+ /// The clipboard sequence number. If you do not have <c>WINSTA_ACCESSCLIPBOARD</c> access to the window station, the function
90
+ /// returns zero.
91
+ /// </returns>
92
+ /// <remarks>
93
+ /// The system keeps a serial number for the clipboard for each window station. This number is incremented whenever the contents of
94
+ /// the clipboard change or the clipboard is emptied. You can track this value to determine whether the clipboard contents have
95
+ /// changed and optimize creating DataObjects. If clipboard rendering is delayed, the sequence number is not incremented until the
96
+ /// changes are rendered.
97
+ /// </remarks>
98
+ public static uint SequenceNumber => GetClipboardSequenceNumber ( ) ;
108
99
109
100
/// <summary>Gets or sets a <see cref="IComDataObject"/> instance from the Windows Clipboard.</summary>
110
101
/// <value>A <see cref="IComDataObject"/> instance.</value>
111
102
static IComDataObject ReadOnlyDataObject
112
103
{
113
104
get
114
105
{
106
+ Init ( ) ;
115
107
int n = stdRetryCnt ;
116
108
HRESULT hr = HRESULT . S_OK ;
117
109
for ( int i = 1 ; i <= n ; i ++ )
@@ -136,24 +128,12 @@ static IComDataObject WritableDataObj
136
128
}
137
129
set
138
130
{
131
+ Init ( ) ;
139
132
TryMultThenThrowIfFailed ( OleSetClipboard , value ) ;
140
133
Flush ( ) ;
141
134
}
142
135
}
143
136
144
- /// <summary>Retrieves the clipboard sequence number for the current window station.</summary>
145
- /// <returns>
146
- /// The clipboard sequence number. If you do not have <c>WINSTA_ACCESSCLIPBOARD</c> access to the window station, the function
147
- /// returns zero.
148
- /// </returns>
149
- /// <remarks>
150
- /// The system keeps a serial number for the clipboard for each window station. This number is incremented whenever the contents of
151
- /// the clipboard change or the clipboard is emptied. You can track this value to determine whether the clipboard contents have
152
- /// changed and optimize creating DataObjects. If clipboard rendering is delayed, the sequence number is not incremented until the
153
- /// changes are rendered.
154
- /// </remarks>
155
- public static uint SequenceNumber => GetClipboardSequenceNumber ( ) ;
156
-
157
137
/// <summary>Clears the clipboard of any data or formatting.</summary>
158
138
public static void Clear ( ) => WritableDataObj = null ;
159
139
@@ -174,7 +154,7 @@ static IComDataObject WritableDataObj
174
154
public static IEnumerable < uint > EnumAvailableFormats ( ) => ReadOnlyDataObject . EnumFormats ( ) . Select ( f => unchecked ( ( uint ) f . cfFormat ) ) ;
175
155
176
156
/// <summary>Carries out the clipboard shutdown sequence. It also releases any IDataObject instances that were placed on the clipboard.</summary>
177
- public static void Flush ( ) { TryMultThenThrowIfFailed ( OleFlushClipboard ) ; writableDataObj = null ; }
157
+ public static void Flush ( ) { Init ( ) ; TryMultThenThrowIfFailed ( OleFlushClipboard ) ; writableDataObj = null ; }
178
158
179
159
/// <summary>Retrieves the window handle of the current owner of the clipboard.</summary>
180
160
/// <returns>
@@ -274,13 +254,14 @@ public static string[] GetFileNameMap()
274
254
/// <summary>Determines whether the clipboard contains data in the specified format.</summary>
275
255
/// <param name="id">A standard or registered clipboard format.</param>
276
256
/// <returns>If the clipboard format is available, the return value is <see langword="true"/>; otherwise <see langword="false"/>.</returns>
277
- public static bool IsFormatAvailable ( uint id ) => ReadOnlyDataObject . IsFormatAvailable ( id ) ; // EnumAvailableFormats().Contains(id);
257
+ public static bool IsFormatAvailable ( uint id ) => ReadOnlyDataObject . IsFormatAvailable ( id ) ;
278
258
279
259
/// <summary>Determines whether the clipboard contains data in the specified format.</summary>
280
260
/// <param name="id">A clipboard format string.</param>
281
261
/// <returns>If the clipboard format is available, the return value is <see langword="true"/>; otherwise <see langword="false"/>.</returns>
282
262
public static bool IsFormatAvailable ( string id ) => IsClipboardFormatAvailable ( RegisterFormat ( id ) ) ;
283
263
264
+ // EnumAvailableFormats().Contains(id);
284
265
/// <summary>Registers a new clipboard format. This format can then be used as a valid clipboard format.</summary>
285
266
/// <param name="format">The name of the new format.</param>
286
267
/// <returns>The registered clipboard format identifier.</returns>
@@ -296,12 +277,12 @@ public static string[] GetFileNameMap()
296
277
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
297
278
/// <param name="data">The binary data in the specified format.</param>
298
279
/// <exception cref="System.ArgumentNullException">data</exception>
299
- public static void SetBinaryData ( uint formatId , byte [ ] data ) => WritableDataObj . SetData ( formatId , data ) ;
280
+ public static void SetBinaryData ( uint formatId , byte [ ] data ) => Setter ( i => i . SetData ( formatId , data ) ) ;
300
281
301
282
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
302
283
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
303
284
/// <param name="data">The data in the format dictated by <paramref name="formatId"/>.</param>
304
- public static void SetData < T > ( uint formatId , T data ) where T : struct => WritableDataObj . SetData ( formatId , data ) ;
285
+ public static void SetData < T > ( uint formatId , T data ) where T : struct => Setter ( i => i . SetData ( formatId , data ) ) ;
305
286
306
287
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
307
288
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
@@ -310,7 +291,7 @@ public static void SetData<T>(uint formatId, IEnumerable<T> values) where T : st
310
291
{
311
292
var pMem = SafeMoveableHGlobalHandle . CreateFromList ( values ) ;
312
293
Win32Error . ThrowLastErrorIfInvalid ( pMem ) ;
313
- WritableDataObj . SetData ( formatId , pMem ) ;
294
+ Setter ( i => i . SetData ( formatId , pMem ) ) ;
314
295
}
315
296
316
297
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
@@ -322,7 +303,7 @@ public static void SetData(uint formatId, IEnumerable<string> values, StringList
322
303
{
323
304
var pMem = SafeMoveableHGlobalHandle . CreateFromStringList ( values , packing , charSet ) ;
324
305
Win32Error . ThrowLastErrorIfInvalid ( pMem ) ;
325
- WritableDataObj . SetData ( formatId , pMem ) ;
306
+ Setter ( i => i . SetData ( formatId , pMem ) ) ;
326
307
}
327
308
328
309
/// <summary>Puts a list of shell items onto the clipboard.</summary>
@@ -337,7 +318,7 @@ public static void SetShellItems(ShellFolder parent, IEnumerable<ShellItem> rela
337
318
if ( parent is null ) throw new ArgumentNullException ( nameof ( parent ) ) ;
338
319
if ( relativeShellItems is null ) throw new ArgumentNullException ( nameof ( relativeShellItems ) ) ;
339
320
SHCreateDataObject ( parent . PIDL , relativeShellItems . Select ( i => i . PIDL ) , default , out var dataObj ) . ThrowIfFailed ( ) ;
340
- OleSetClipboard ( dataObj ) . ThrowIfFailed ( ) ;
321
+ WritableDataObj = dataObj ;
341
322
}
342
323
343
324
/// <summary>Sets multiple text types to the Clipboard.</summary>
@@ -354,13 +335,13 @@ public static void SetText(string text, string htmlText = null, string rtfText =
354
335
/// <summary>Sets a specific text type to the Clipboard.</summary>
355
336
/// <param name="value">The text value.</param>
356
337
/// <param name="format">The clipboard text format to set.</param>
357
- public static void SetText ( string value , TextDataFormat format ) => WritableDataObj . SetData ( Txt2Id ( format ) , value ) ;
338
+ public static void SetText ( string value , TextDataFormat format ) => Setter ( i => i . SetData ( Txt2Id ( format ) , value ) ) ;
358
339
359
340
/// <summary>Sets a URL with optional title to the clipboard.</summary>
360
341
/// <param name="url">The URL.</param>
361
342
/// <param name="title">The title. This value can be <see langword="null"/>.</param>
362
343
/// <exception cref="ArgumentNullException">url</exception>
363
- public static void SetUrl ( string url , string title = null ) => WritableDataObj . SetUrl ( url , title ) ;
344
+ public static void SetUrl ( string url , string title = null ) => Setter ( i => i . SetUrl ( url , title ) ) ;
364
345
365
346
/// <summary>Obtains data from a source data object.</summary>
366
347
/// <typeparam name="T">The type of the object being retrieved.</typeparam>
@@ -373,6 +354,39 @@ public static void SetText(string text, string htmlText = null, string rtfText =
373
354
/// <returns><see langword="true"/> if data is available and retrieved; otherwise <see langword="false"/>.</returns>
374
355
public static bool TryGetData < T > ( uint formatId , out T obj , int index = - 1 ) => ReadOnlyDataObject . TryGetData ( formatId , out obj , index ) ;
375
356
357
+ private static void Init ( ) { if ( ! oleInit ) { oleInit = OleInitialize ( ) . Succeeded ; } }
358
+
359
+ private static void Setter ( Action < IComDataObject > action )
360
+ {
361
+ var ido = WritableDataObj ;
362
+ action ( ido ) ;
363
+ WritableDataObj = ido ;
364
+ }
365
+
366
+ private static bool TryMultThenThrowIfFailed ( Func < HRESULT > func , int n = stdRetryCnt )
367
+ {
368
+ HRESULT hr = HRESULT . S_OK ;
369
+ for ( int i = 1 ; i <= n ; i ++ )
370
+ {
371
+ hr = func ( ) ;
372
+ if ( hr . Failed && i == n )
373
+ throw hr . GetException ( ) ;
374
+ }
375
+ return hr == HRESULT . S_OK ;
376
+ }
377
+
378
+ private static bool TryMultThenThrowIfFailed ( Func < IComDataObject , HRESULT > func , IComDataObject o , int n = stdRetryCnt )
379
+ {
380
+ HRESULT hr = HRESULT . S_OK ;
381
+ for ( int i = 1 ; i <= n ; i ++ )
382
+ {
383
+ hr = func ( o ) ;
384
+ if ( hr . Failed && i == n )
385
+ throw hr . GetException ( ) ;
386
+ }
387
+ return hr == HRESULT . S_OK ;
388
+ }
389
+
376
390
private static uint Txt2Id ( TextDataFormat tf ) => tf switch
377
391
{
378
392
TextDataFormat . Text => CLIPFORMAT . CF_TEXT ,
0 commit comments