Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,32 @@ internal static HRESULT Clear(
return result;
}

/// <summary>
/// Flushes data to the Clipboard.
/// </summary>
/// <returns>An <see cref="HRESULT"/> indicating the success or failure of the operation.</returns>
internal static HRESULT Flush(
int retryTimes = OleRetryCount,
int retryDelay = OleRetryDelay)
{
TOleServices.EnsureThreadState();

HRESULT result;
int retryCount = retryTimes;

while ((result = TOleServices.OleFlushClipboard()).Failed)
{
if (--retryCount < 0)
{
break;
}

Thread.Sleep(millisecondsTimeout: retryDelay);
}

return result;
}

/// <summary>
/// Attempts to set the specified data on the Clipboard.
/// </summary>
Expand Down Expand Up @@ -104,7 +130,8 @@ internal static HRESULT SetData(
/// <param name="proxyDataObject">The proxy data object retrieved from the Clipboard.</param>
/// <param name="originalObject">The original object retrieved from the Clipboard, if available.</param>
/// <returns>An <see cref="HRESULT"/> indicating the success or failure of the operation.</returns>
public static HRESULT TryGetData(
/// <inheritdoc cref="SetData(IComVisibleDataObject, bool, int, int)"/>
internal static HRESULT TryGetData(
out ComScope<IDataObject> proxyDataObject,
out object? originalObject,
int retryTimes = OleRetryCount,
Expand Down Expand Up @@ -146,6 +173,38 @@ public static HRESULT TryGetData(
return result;
}

/// <summary>
/// Returns true if the given <paramref name="object"/> is currently on the Clipboard.
/// </summary>
/// <remarks>
/// <para>This is meant to emulate the OleIsCurrentClipboard API.</para>
/// </remarks>
/// <param name="object">The object to check.</param>
/// <returns>'true' if <paramref name="object"/> is currently on the Clipboard.</returns>
/// <inheritdoc cref="SetData(IComVisibleDataObject, bool, int, int)"/>
internal static bool IsObjectOnClipboard(
object @object,
int retryTimes = OleRetryCount,
int retryDelay = OleRetryDelay)
{
if (@object is null)
{
return false;
}

HRESULT result = TryGetData(
out ComScope<IDataObject> proxyDataObject,
out object? originalObject,
retryTimes,
retryDelay);

// Need to ensure we release the ref count on the proxy object.
using (proxyDataObject)
{
return result.Succeeded && @object == originalObject;
}
}

/// <summary>
/// Returns the data that is currently on the clipboard as the platform specified <typeparamref name="TIDataObject"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,26 @@ static unsafe bool TryGetBitmapData(Com.IDataObject* dataObject, [NotNullWhen(tr
tymed = (uint)TYMED.TYMED_GDI
};

STGMEDIUM medium = default;

if (dataObject->QueryGetData(formatEtc).Succeeded)
HRESULT result = dataObject->QueryGetData(formatEtc);
if (result.Failed)
{
HRESULT hr = dataObject->GetData(formatEtc, out medium);

// One of the ways this can happen is when we attempt to put binary formatted data onto the
// clipboard, which will succeed as Windows ignores all errors when putting data on the clipboard.
// The data state, however, is not good, and this error will be returned by Windows when asking to
// get the data out.
Debug.WriteLineIf(hr == HRESULT.CLIPBRD_E_BAD_DATA, "CLIPBRD_E_BAD_DATA returned when trying to get clipboard data.");
return false;
}

result = dataObject->GetData(formatEtc, out STGMEDIUM medium);

// One of the ways this can happen is when we attempt to put binary formatted data onto the
// clipboard, which will succeed as Windows ignores all errors when putting data on the clipboard.
// The data state, however, is not good, and this error will be returned by Windows when asking to
// get the data out.
Debug.WriteLineIf(result == HRESULT.CLIPBRD_E_BAD_DATA, "CLIPBRD_E_BAD_DATA returned when trying to get clipboard data.");

try
{
// GDI+ doesn't own this HBITMAP, but we can't delete it while the object is still around. So we
// have to do the really expensive thing of cloning the image so we can release the HBITMAP.
if ((uint)medium.tymed == (uint)TYMED.TYMED_GDI
if (result.Succeeded
&& (uint)medium.tymed == (uint)TYMED.TYMED_GDI
&& !medium.hGlobal.IsNull
&& Image.FromHbitmap(medium.hGlobal) is Bitmap clipboardBitmap)
{
Expand Down
Loading