Skip to content

Instantly share code, notes, and snippets.

@RichardD2
Created April 25, 2022 07:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RichardD2/888a166efe30f68ec1404822865a88e0 to your computer and use it in GitHub Desktop.
Save RichardD2/888a166efe30f68ec1404822865a88e0 to your computer and use it in GitHub Desktop.
Windows 10 Clipboard Utilities
using System;
using System.Windows;
public static class ClipboardHelper
{
public static void SetString(string value)
{
if (!DataTransferHelper.TrySetString(value))
{
Clipboard.SetDataObject(value);
}
}
}
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;
}
}
}
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