C
C#2mo ago
faint

First use of Winforms' Clipboard methods results in an ExternalException

Was debugging a Winforms app bug where the following code would fail but only if it is the first time of using any Clipboard method.
private static readonly object ClipboardLock = new object();
private static bool CopyData(IDataObject data, bool copy = true)
{
if (data == null)
return false;

lock (ClipboardLock)
Clipboard.SetDataObject(data, copy, 20, 100);

return true;
}
private static readonly object ClipboardLock = new object();
private static bool CopyData(IDataObject data, bool copy = true)
{
if (data == null)
return false;

lock (ClipboardLock)
Clipboard.SetDataObject(data, copy, 20, 100);

return true;
}
When used for the first time since the app launch, it throws the following exception: System.Runtime.InteropServices.ExternalException (0x800401D0): Requested Clipboard operation did not succeed Interestingly enough, if you just add a random Clipboard method BEFORE the SetDataObject call, everything works fine without any errors Could someone help me to figure out what might be causing it? Thanks in advance!
61 Replies
Trinitek
Trinitek2mo ago
hresult 800401D0 is CLIPBRD_E_CANT_OPEN. do you have a full stacktrace for this?
faint
faintOP2mo ago
Best I can find in the console is this:
System.Runtime.InteropServices.ExternalException (0x800401D0): Requested Clipboard operation did not succeed.
at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay)
at ShareX.HelpersLib.ClipboardHelpers.CopyData(IDataObject data, Boolean copy) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 56
at ShareX.HelpersLib.ClipboardHelpers.CopyImageDefaultFillBackground(Image img, Color background) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 156
at ShareX.HelpersLib.ClipboardHelpers.CopyImage(Image img, String fileName) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 123
System.Runtime.InteropServices.ExternalException (0x800401D0): Requested Clipboard operation did not succeed.
at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay)
at ShareX.HelpersLib.ClipboardHelpers.CopyData(IDataObject data, Boolean copy) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 56
at ShareX.HelpersLib.ClipboardHelpers.CopyImageDefaultFillBackground(Image img, Color background) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 156
at ShareX.HelpersLib.ClipboardHelpers.CopyImage(Image img, String fileName) in C:\Users\faint\RiderProjects\ShareX\ShareX.HelpersLib\Helpers\ClipboardHelpers.cs:line 123
ClipboardHelpers.cs:line 56 is the Clipboard.SetDataObject call
Trinitek
Trinitek2mo ago
curious, does this also happen after you reboot the computer?
faint
faintOP2mo ago
Well, I as a user of this app have been having this bug for like a week or even more, and I definitely have rebooted. Or do you mean try it on like a fresh boot, so there're no apps open and stuff?
Trinitek
Trinitek2mo ago
yes, fresh reboot the Windows clipboard has a concept of ownership where another app can lock it, and if there's a bug, it can stay locked
faint
faintOP2mo ago
Also fwiw it definitely wasn't a thing until some point. I tried to remember what could have been updated in the last few months, and the one that seems very interesting is updating all the projects to .NET 9.0 from .NET 4.8
No description
Trinitek
Trinitek2mo ago
in the past I have used a tool like https://www.nirsoft.net/utils/inside_clipboard.html to reveal the current owner
faint
faintOP2mo ago
Oh yeah I tried using that as well but honestly couldn't really figure out how to use it
Trinitek
Trinitek2mo ago
yes there could be a behavior change here
faint
faintOP2mo ago
Alright, I'll try to restart real quick, be right back
Trinitek
Trinitek2mo ago
the owner would be in the bottom, like "firefox", with the PID next to it
No description
faint
faintOP2mo ago
That’s interesting, it works now that I’ve closed as many apps as I could Nevermind, not anymore :/
faint
faintOP2mo ago
That's what I see there
No description
faint
faintOP2mo ago
Selecting it doesn't show anything
Trinitek
Trinitek2mo ago
no owner info, hmm
faint
faintOP2mo ago
Though the format is afaict set correctly
Trinitek
Trinitek2mo ago
well, CF_BITMAPs are supposed to have image data and not be size zero, so something has gone wrong somewhere this is ShareX? it may or may not be ShareX's fault
faint
faintOP2mo ago
Yeah, just interesting that it managed to get the data format right, but the data itself is lost :smilethink: Yeah, it is, I'm trying to debug why it doesn't work
Trinitek
Trinitek2mo ago
oh I see, this is after SetData and it throws what was the owner before you tried to copy?
faint
faintOP2mo ago
Reopened the app and I see this:
No description
faint
faintOP2mo ago
After screenshotting and making that method call SetDataObject, it's now just CF_BITMAP Weird, after a rebuild I can't repro this at all :/
Trinitek
Trinitek2mo ago
I still suspect another unrelated app is opening the clipboard and failing to close it
faint
faintOP2mo ago
I think I broke InsideClipboard lol. It won't load at all now
Trinitek
Trinitek2mo ago
it has a tendency to freeze if one of the listed formats has a massive amount of data in it it tries to materialize the whole thing at once
faint
faintOP2mo ago
Is there any way I can see what app is opening it? If it's the owner field in InsideClipboard, then it happens with any app
Trinitek
Trinitek2mo ago
yeah that's the owner field there's nothing obviously wrong with the WinForms Clipboard class on net9, that's PInvoking the OLE Clipboard class provided by Win32, which is exactly what Framework did the stacktrace eventually ends up at https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms/System/Windows/Forms/OLE/WinFormsOleServices.cs#L154
faint
faintOP2mo ago
@T'tk Hey, in case you want to know, I found a very interesting thing. I decided to make a sample WinForms app to see if I can repro this bug and I could do so. Then I got curious if downgrading to an older .NET version (specifically net48) would fix it (because that's the only big update I can update that ShareX has had recently), and it did fix this bug. So, did I just discover some .NET / WinForms bug? :wires: nevermind, .net 4.8's winforms just didnt throw any exceptions for me :/ the issue is still there
Trinitek
Trinitek2mo ago
. I'm still of the opinion it's another app on your system
faint
faintOP2mo ago
I tried using GetOpenClipboardWindow to see what app is using clipboard and it didn't show any :/
GetOpenClipboardWindow function (winuser.h) - Win32 apps
Retrieves the handle to the window that currently has the clipboard open.
Trinitek
Trinitek2mo ago
does https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclipboardowner say anything? also, how big is the image that you're trying to copy?
faint
faintOP2mo ago
It happens with any data type. For instance, a text I was trying to copy in my sample app is only 9 characters It returned some HWND, though I'm not sure how I can check what window it is from this HWND
Trinitek
Trinitek2mo ago
there's an app called Spy++ that comes with the Visual Studio C++ tools or, I can get you a code snippet to locate the process one moment
faint
faintOP2mo ago
appreciate it!
Trinitek
Trinitek2mo ago
public sealed record ClipboardInfo
{
public required IDataObject DataObject { get; init; }
public required string[] Formats { get; init; }
public required string? OwnerProcessName { get; init; }
public required int? OwnerProcessId { get; init; }
public required int? OwnerHwnd { get; init; }
public required string? OwnerHwndText { get; init; }

public unsafe static ClipboardInfo Create()
{
IDataObject dataObject = System.Windows.Clipboard.GetDataObject();
string[] formats = dataObject.GetFormats(autoConvert: false);
string? ownerProcessName;
int? ownerProcessId;
int? ownerHwnd;
string? ownerHwndText;

var hwnd = Windows.Win32.PInvoke.GetClipboardOwner();

if (hwnd.IsNull)
{
ownerProcessName = null;
ownerProcessId = null;
ownerHwnd = null;
ownerHwndText = null;

goto end;
}

ownerHwnd = (int)hwnd.Value;

Span<char> hwndText = new char[32];
int textLength = Windows.Win32.PInvoke.GetWindowText(hwnd, hwndText);

ownerHwndText = textLength == 0
? null
: new string(hwndText[..textLength]);

uint pid = 0;

uint threadId = Windows.Win32.PInvoke.GetWindowThreadProcessId(hwnd, &pid);

if (threadId == 0)
{
ownerProcessId = null;
ownerProcessName = null;

goto end;
}

ownerProcessId = (int)pid;

using (var process = Process.GetProcessById((int)pid))
{
ownerProcessName = process.ProcessName;
}

end:
return new()
{
DataObject = dataObject,
Formats = formats,
OwnerProcessName = ownerProcessName,
OwnerProcessId = ownerProcessId,
OwnerHwnd = ownerHwnd,
OwnerHwndText = ownerHwndText
};
}
}
public sealed record ClipboardInfo
{
public required IDataObject DataObject { get; init; }
public required string[] Formats { get; init; }
public required string? OwnerProcessName { get; init; }
public required int? OwnerProcessId { get; init; }
public required int? OwnerHwnd { get; init; }
public required string? OwnerHwndText { get; init; }

public unsafe static ClipboardInfo Create()
{
IDataObject dataObject = System.Windows.Clipboard.GetDataObject();
string[] formats = dataObject.GetFormats(autoConvert: false);
string? ownerProcessName;
int? ownerProcessId;
int? ownerHwnd;
string? ownerHwndText;

var hwnd = Windows.Win32.PInvoke.GetClipboardOwner();

if (hwnd.IsNull)
{
ownerProcessName = null;
ownerProcessId = null;
ownerHwnd = null;
ownerHwndText = null;

goto end;
}

ownerHwnd = (int)hwnd.Value;

Span<char> hwndText = new char[32];
int textLength = Windows.Win32.PInvoke.GetWindowText(hwnd, hwndText);

ownerHwndText = textLength == 0
? null
: new string(hwndText[..textLength]);

uint pid = 0;

uint threadId = Windows.Win32.PInvoke.GetWindowThreadProcessId(hwnd, &pid);

if (threadId == 0)
{
ownerProcessId = null;
ownerProcessName = null;

goto end;
}

ownerProcessId = (int)pid;

using (var process = Process.GetProcessById((int)pid))
{
ownerProcessName = process.ProcessName;
}

end:
return new()
{
DataObject = dataObject,
Formats = formats,
OwnerProcessName = ownerProcessName,
OwnerProcessId = ownerProcessId,
OwnerHwnd = ownerHwnd,
OwnerHwndText = ownerHwndText
};
}
}
the PInvokes are code-generated by CsWin32, so I can't include those
faint
faintOP2mo ago
uhhhh
No description
Trinitek
Trinitek2mo ago
you need to add a reference to CsWin32 and then add the function names in a file called NativeMethods.txt that file should contain
GetClipboardOwner
GetWindowText
GetWindowThreadProcessId
GetClipboardOwner
GetWindowText
GetWindowThreadProcessId
exactly as written oh yes, also System.Windows.Clipboard is a WPF type, so you may need to add a <UseWPF>true</UseWPF> to your csproj
faint
faintOP2mo ago
Can I just use the Winforms' alternative?
Trinitek
Trinitek2mo ago
I'm just using that to get a string[] of the available format names, so I think so yes
faint
faintOP2mo ago
Sorry for long response, my VPN died (without it I cant access Discord). I modified your code a bit to remove the GetDataObject call (because as you remember any clipboard call before SetDataObject fixes the issue). The process name shows the one I copied from the last. But regardless of the app, it still never works
Trinitek
Trinitek2mo ago
alright you had a workaround earlier, where you could invoke another clipboard function, and then the copy would work
faint
faintOP2mo ago
yeah
Trinitek
Trinitek2mo ago
does that consistently work?
faint
faintOP2mo ago
hm let me try it a few times Yes, seem to be working all the time
Trinitek
Trinitek2mo ago
I think I'm out of things to suggest to "fix" it without the workaround, but I'll point you to this thread which has some relevant discussion https://github.com/dotnet/wpf/issues/9901 nevermind that's on the WPF repo, the issue is actually coming from something lower in the system
faint
faintOP2mo ago
Yeah, saw this one. I tried doing this but opening it in any editor made my system go crazy :slughollow:
GitHub
Clipboard.SetText fails with CLIPBRD_E_CANT_OPEN · Issue #9901 · ...
Description Most of the time, the call to Clipboard.SetText takes several hundred milliseconds and then fails with CLIPBRD_E_CANT_OPEN in the flush operation. The WinForms implementation works fine...
No description
Trinitek
Trinitek2mo ago
uh. is that 19 GB
faint
faintOP2mo ago
yeah that's the thing - it's only 900mb
Trinitek
Trinitek2mo ago
900 mb, okay that's still really big no wonder InsideClipboard froze earlier
faint
faintOP2mo ago
I also tried doing something like cat output.txt | clip but it didn't work either well, that time it wasn't that big
Trinitek
Trinitek2mo ago
and that's after copying from ShareX?
faint
faintOP2mo ago
iirc yes
Trinitek
Trinitek2mo ago
could you try doing CopyData with copy = false
faint
faintOP2mo ago
yeah well that will work because the stacktrace points at the use of OleFlushClipboard and if we set copy to false, it will skip that part
faint
faintOP2mo ago
the error occurs here:
No description
Trinitek
Trinitek2mo ago
shrug. I guess the data is too large to flush reliably.
faint
faintOP2mo ago
well I don't really think this is that big :wires:
No description
Trinitek
Trinitek2mo ago
lol
faint
faintOP2mo ago
Just curious, can you reproduce this issue on your machine?
Trinitek
Trinitek2mo ago
I've never gotten this error unless another app had the clipboard open, and that's something that's usually handled with a retry idk. your clipboard is fucked. that's all I've got.
faint
faintOP2mo ago
windows my beloved. I guess I’ll have to reinstall it because I’ve noticed some other weird bugs like how opening the settings app freezes my ENTIRE OS for like five seconds Anyways uh I appreciate your help, thank you so much for your time and enthusiasm! Have a good one I guess I can leave this thread open as technically it hasn’t been solved?
Trinitek
Trinitek2mo ago
you can leave it

Did you find this page helpful?