[Solution] .NET Clipboard object always acts like clipboard is empty when it isn't
-
This was a very annoying bug in my program to pinpoint, partly because of an annoying bug in .NET which was leading me down the wrong path. First of all, I'll just say up front: You cannot use Clipboard from any thread but your main thread. Since you ran into this problem you are likely trying to use it from a BackgroundWorker or Thread or whatever. create a delegate for Clipboard.GetDataObject and .Invoke it and your problem will go away. So for my specific problem... I have a screenshot tray program. Even though I didn't need a separate thread at first (.NET seems to automatically create a new thread for tray icons and menus and manages it all for you... so it responds even when your program is busy) but then I added notfication boxes which would have to animate and such while background work was happening, so I needed a separate thread for the work. I moved all my existing code into a new thread, including a Clipboard.GetImage call. It stopped working but I didn't figure it out until later since I rarely used the part of the program that called it. So i didn't immediately make the connection. I went through the annoying "clipboard is empty? No it isn't, clipbook right here shows an image" loop of madness and gave up. I came back later and decided to step through the actual code behind .NET using Lutz Roeder's awesome .NET Reflector. All Clipboard "Get" commands go through GetDataObject, so let's take a look:
public static IDataObject GetDataObject()
{
IntSecurity.ClipboardRead.Demand();
if (Application.OleRequired() == ApartmentState.STA)
{
return GetDataObject(10, 100);
}
if (Application.MessageLoop)
{
throw new ThreadStateException(SR.GetString("ThreadMustBeSTA"));
}
return null;
}I know I have sufficient security privileges to read the clipboard (and if I didn't, .Demand causes an exception to be thrown). My next move was to jump into the internal GetDataObject(10, 100) but that was a dead end... I went back and scanned this function more carefully. Do you see it? I evaluated Application.OleRequired() in my own program... it was ApartmentState.MTA. Oops. I'm still not sure why there was no exception thrown... I believe that last if test shouldn't even be there, you don't need a MessageLoop to throw exceptions (anyone have a possible explanation why the code is like this, other than someone forgot to take out the if test?). Anyways, wouldn't MessageLoop return false on worker Threads anyway? Defini
-
This was a very annoying bug in my program to pinpoint, partly because of an annoying bug in .NET which was leading me down the wrong path. First of all, I'll just say up front: You cannot use Clipboard from any thread but your main thread. Since you ran into this problem you are likely trying to use it from a BackgroundWorker or Thread or whatever. create a delegate for Clipboard.GetDataObject and .Invoke it and your problem will go away. So for my specific problem... I have a screenshot tray program. Even though I didn't need a separate thread at first (.NET seems to automatically create a new thread for tray icons and menus and manages it all for you... so it responds even when your program is busy) but then I added notfication boxes which would have to animate and such while background work was happening, so I needed a separate thread for the work. I moved all my existing code into a new thread, including a Clipboard.GetImage call. It stopped working but I didn't figure it out until later since I rarely used the part of the program that called it. So i didn't immediately make the connection. I went through the annoying "clipboard is empty? No it isn't, clipbook right here shows an image" loop of madness and gave up. I came back later and decided to step through the actual code behind .NET using Lutz Roeder's awesome .NET Reflector. All Clipboard "Get" commands go through GetDataObject, so let's take a look:
public static IDataObject GetDataObject()
{
IntSecurity.ClipboardRead.Demand();
if (Application.OleRequired() == ApartmentState.STA)
{
return GetDataObject(10, 100);
}
if (Application.MessageLoop)
{
throw new ThreadStateException(SR.GetString("ThreadMustBeSTA"));
}
return null;
}I know I have sufficient security privileges to read the clipboard (and if I didn't, .Demand causes an exception to be thrown). My next move was to jump into the internal GetDataObject(10, 100) but that was a dead end... I went back and scanned this function more carefully. Do you see it? I evaluated Application.OleRequired() in my own program... it was ApartmentState.MTA. Oops. I'm still not sure why there was no exception thrown... I believe that last if test shouldn't even be there, you don't need a MessageLoop to throw exceptions (anyone have a possible explanation why the code is like this, other than someone forgot to take out the if test?). Anyways, wouldn't MessageLoop return false on worker Threads anyway? Defini