Created
April 25, 2022 07:42
-
-
Save RichardD2/888a166efe30f68ec1404822865a88e0 to your computer and use it in GitHub Desktop.
Windows 10 Clipboard Utilities
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Windows; | |
public static class ClipboardHelper | |
{ | |
public static void SetString(string value) | |
{ | |
if (!DataTransferHelper.TrySetString(value)) | |
{ | |
Clipboard.SetDataObject(value); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Reflection; | |
using System.Threading; | |
public static class DataTransferHelper | |
{ | |
private const string Namespace = "Windows.ApplicationModel.DataTransfer"; | |
private const BindingFlags StaticBindingFlags = BindingFlags.Public | BindingFlags.Static; | |
private const BindingFlags InstanceBindingFlags = BindingFlags.Public | BindingFlags.Instance; | |
private static class Clipboard | |
{ | |
private const string ClipboardTypeName = Namespace + ".Clipboard"; | |
private static readonly Type clipboardType; | |
private static readonly MethodInfo isHistoryEnabledMethod; | |
private static readonly MethodInfo isRoamingEnabledMethod; | |
private static readonly MethodInfo setContentWithOptionsMethod; | |
private static readonly MethodInfo flushMethod; | |
static Clipboard() | |
{ | |
if (!WindowsUtilities.IsAtLeastWindows10) return; | |
clipboardType = GetUwpType(ClipboardTypeName); | |
if (clipboardType is null) return; | |
isHistoryEnabledMethod = clipboardType.GetMethod("IsHistoryEnabled", StaticBindingFlags); | |
isRoamingEnabledMethod = clipboardType.GetMethod("IsRoamingEnabled", StaticBindingFlags); | |
setContentWithOptionsMethod = clipboardType.GetMethod("SetContentWithOptions", StaticBindingFlags); | |
flushMethod = clipboardType.GetMethod("Flush", StaticBindingFlags); | |
} | |
public static bool IsValid => clipboardType is not null && isHistoryEnabledMethod is not null && isRoamingEnabledMethod is not null && setContentWithOptionsMethod is not null; | |
public static bool IsHistoryEnabled() => isHistoryEnabledMethod is not null && (bool)isHistoryEnabledMethod.Invoke(null, null); | |
public static bool IsRoamingEnabled() => isRoamingEnabledMethod is not null && (bool)isRoamingEnabledMethod.Invoke(null, null); | |
public static bool SetContentWithOptions(object dataPackage, object contentOptions) | |
{ | |
if (setContentWithOptionsMethod is null) return false; | |
object[] parameters = { dataPackage, contentOptions }; | |
return InvokeAndRetry(() => (bool)setContentWithOptionsMethod.Invoke(null, parameters)); | |
} | |
public static void Flush() | |
{ | |
if (flushMethod is null) return; | |
InvokeAndRetry(() => { flushMethod.Invoke(null, null); return true; }); | |
} | |
} | |
private static class ClipboardContentOptions | |
{ | |
private const string ClipboardContentOptionsTypeName = Namespace + ".ClipboardContentOptions"; | |
private static readonly Type clipboardContentOptionsType; | |
private static readonly PropertyInfo isAllowedInHistory; | |
private static readonly PropertyInfo isRoamable; | |
static ClipboardContentOptions() | |
{ | |
if (!WindowsUtilities.IsAtLeastWindows10) return; | |
clipboardContentOptionsType = GetUwpType(ClipboardContentOptionsTypeName); | |
if (clipboardContentOptionsType is null) return; | |
isAllowedInHistory = clipboardContentOptionsType.GetProperty("IsAllowedInHistory", InstanceBindingFlags); | |
isRoamable = clipboardContentOptionsType.GetProperty("IsRoamable", InstanceBindingFlags); | |
} | |
public static bool IsValid => clipboardContentOptionsType is not null && isAllowedInHistory is not null && isRoamable is not null; | |
[NotNull] | |
public static object CreateInstance() | |
{ | |
if (!IsValid) throw new InvalidOperationException(); | |
object clipboardContentOptions = Activator.CreateInstance(clipboardContentOptionsType); | |
isAllowedInHistory.SetValue(clipboardContentOptions, false, null); | |
isRoamable.SetValue(clipboardContentOptions, false, null); | |
return clipboardContentOptions; | |
} | |
} | |
private static class DataPackage | |
{ | |
private const string DataPackageTypeName = Namespace + ".DataPackage"; | |
private const string ClipboardIgnoreFormatName = "Clipboard Viewer Ignore"; | |
private static readonly Type dataPackageType; | |
private static readonly MethodInfo setDataMethod; | |
static DataPackage() | |
{ | |
if (!WindowsUtilities.IsAtLeastWindows10) return; | |
dataPackageType = GetUwpType(DataPackageTypeName); | |
if (dataPackageType is null) return; | |
setDataMethod = dataPackageType.GetMethod("SetData", InstanceBindingFlags); | |
} | |
public static bool IsValid => dataPackageType is not null && setDataMethod is not null; | |
[CanBeNull] | |
public static object CreateInstance(Func<Type, object, bool> setData) | |
{ | |
if (!IsValid) throw new InvalidOperationException(); | |
object dataPackage = Activator.CreateInstance(dataPackageType); | |
setDataMethod.Invoke(dataPackage, new object[] { ClipboardIgnoreFormatName, string.Empty }); | |
return setData(dataPackageType, dataPackage) ? dataPackage : null; | |
} | |
} | |
public static bool IsEnabled => Clipboard.IsValid && ClipboardContentOptions.IsValid && DataPackage.IsValid; | |
[CanBeNull] | |
private static Type GetUwpType([NotNull] string typeName) | |
{ | |
// https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/winrtclassactivator.cs | |
return Type.GetType(typeName + ", Windows, ContentType=WindowsRuntime", false); | |
} | |
private static bool InvokeAndRetry([NotNull] Func<bool> fn) | |
{ | |
// https://referencesource.microsoft.com/#system.windows.forms/winforms/managed/system/winforms/Clipboard.cs | |
for (int i = 0; i < 15; ++i) | |
{ | |
try | |
{ | |
if (fn()) | |
{ | |
return true; | |
} | |
} | |
catch (Exception ex) | |
{ | |
Trace.TraceError(ex.ToString()); | |
} | |
Thread.Sleep(100); | |
} | |
return false; | |
} | |
private static bool TrySetData([NotNull] Func<Type, object, bool> setData) | |
{ | |
if (setData is null) throw new ArgumentNullException(nameof(setData)); | |
if (!WindowsUtilities.IsAtLeastWindows10 || !IsEnabled) return false; | |
if (!Clipboard.IsHistoryEnabled() && !Clipboard.IsRoamingEnabled()) return false; | |
object clipboardContentOptions = ClipboardContentOptions.CreateInstance(); | |
object dataPackage = DataPackage.CreateInstance(setData); | |
if (dataPackage is null) return false; | |
if (!Clipboard.SetContentWithOptions(dataPackage, clipboardContentOptions)) return false; | |
Clipboard.Flush(); | |
return true; | |
} | |
public static bool TrySetString([NotNull] string value) | |
{ | |
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); | |
try | |
{ | |
return TrySetData((dataPackageType, dataPackage) => | |
{ | |
MethodInfo mi = dataPackageType.GetMethod("SetText", InstanceBindingFlags); | |
if (mi is null) return false; | |
mi.Invoke(dataPackage, new object[] { value }); | |
return true; | |
}); | |
} | |
catch (Exception ex) | |
{ | |
Trace.TraceError(ex.ToString()); | |
return false; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using Microsoft.Win32; | |
public static class WindowsUtilities | |
{ | |
static WindowsUtilities() | |
{ | |
var osVersion = Environment.OSVersion.Version; | |
if (osVersion.Major >= 10) | |
{ | |
IsAtLeastWindows10 = true; | |
} | |
else if (osVersion.Major == 6 && osVersion.Minor == 2) | |
{ | |
try | |
{ | |
// Environment.OSVersion is reliable only up to version 6.2; | |
// https://docs.microsoft.com/en-gb/windows/desktop/SysInfo/operating-system-version | |
using var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", false); | |
if (key is not null) | |
{ | |
string version = key.GetValue("CurrentMajorVersionNumber", string.Empty).ToString(); | |
if (!string.IsNullOrWhiteSpace(version) && uint.TryParse(version, out var v)) | |
{ | |
IsAtLeastWindows10 = v >= 10; | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
Trace.TraceError(ex.ToString()); | |
} | |
} | |
} | |
public static bool IsAtLeastWindows10 { get; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment