4

I'm trying to use the System.Windows.Clipboard class to obtain an image from the clipboard:

var bitmapSource = System.Windows.Clipboard.GetImage();

When the image is copied via the PrintScreen key, it works fine. However, when the image is copied from a medical application, I get the following exception:

System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
   at System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC& format, STGMEDIUM& medium)
   at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
   at System.Windows.DataObject.OleConverter.GetDataFromOleOther(String format, DVASPECT aspect, Int32 index)
   at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert, DVASPECT aspect, Int32 index)
   at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert)

The image pastes fine on Paint and Word, so the image is being copied properly to the clipboard. It's not a huge image, so I'm definitely not running out of memory. Any ideas?

Calling Clipboard.GetDataObject().GetFormats() returns the following:

{string[11]}
    [0]: "Rich Text Format"
    [1]: "MetaFilePict"
    [2]: "PNG+Office Art"
    [3]: "Office Drawing Shape Format"
    [4]: "DeviceIndependentBitmap"
    [5]: "Bitmap"
    [6]: "System.Drawing.Bitmap"
    [7]: "System.Windows.Media.Imaging.BitmapSource"
    [8]: "Format17"
    [9]: "EnhancedMetafile"
    [10]: "System.Drawing.Imaging.Metafile"

I tried Clipboard.GetData(format) for each of the formats above, and the only ones that returned a non-null object were "PNG+Office Art", "Office Drawing Shape Format", "Format17", and "EnhancedMetafile".

redcurry
  • 2,381
  • 2
  • 24
  • 38
  • Sounds like a job for [serialization](https://stackoverflow.com/questions/6999142/wpf-insufficient-memory-when-doing-copy-paste-vs-drag-drop-with-view-model-dat) (though not sure it's a dupe in this instance). – Chris W. Dec 19 '18 at 17:33
  • @ChrisW. The thing is that my application doesn't copy anything to the clipboard; it only pastes, so the data should already be serialized. – redcurry Dec 19 '18 at 17:36
  • Note that GDI+ gives an `OutOfMemoryException` for a huge load of things going wrong that are completely unrelated to actually being low on memory. – Nyerguds Dec 19 '18 at 17:38
  • @Nyerguds Yes, I don't believe it's memory-related. – redcurry Dec 19 '18 at 17:41
  • Have you actually added a reference to `System.Drawing.Image`? Because, you use `var` as type shortcut and called the variable `bitmapSource`... but `Clipboard.GetImage()` does not return a `System.Windows.Media.Imaging.BitmapSource` object. – Nyerguds Dec 19 '18 at 17:43
  • @Nyerguds The `System.Windows.Clipboard` class I'm using (in PresentationCore) does return a `System.Windows.Media.Imaging.BitmapSource` (by the `GetImage()` method). – redcurry Dec 19 '18 at 17:45
  • Ah, I see, sorry. Easy to confuse those two... – Nyerguds Dec 19 '18 at 17:50
  • Try using other types, like `System.Windows.Interop.InteropBitmap` and see if error persists. – Michał Turczyn Dec 19 '18 at 17:55
  • @MichałTurczyn That type didn't work, but others did (see my comments on Gabriel's answer). – redcurry Dec 19 '18 at 18:15
  • This is not a .NET exception, underlying COM error code is E_OUTOFMEMORY. Which means whatever the programmer of that medical app wanted it to mean. Hard to give specific advice when you don't show what Clipboard.GetDataObject().GetFormats() returns. Some odds that this app only wants to support delayed rendering given that medical images are normally quite large. – Hans Passant Dec 19 '18 at 18:37
  • @HansPassant Why is it not a .NET exception? It's coming from System.Windows.Clipboard, and the error code has nothing to do with the programmer of the medical app. I've added the formats returned in my answer. – redcurry Dec 19 '18 at 18:53

1 Answers1

3

I believe your answer is here. In short:

the conclusion is that if you are working with the Clipboard in WPF and you are getting System.OutOfMemoryExceptions that don’t seem to make any sense, then you’ve probably forgotten to add the SerializableAttribute to whatever class you placed on the Clipboard.

So is this medical application your application? Because it would seem the problem is with how the image is put in the clipboard, rather than how the image is retrieved.

Update: Since it is not your application, then you will likely have to put up with their mistake (or the mistakes in Clipboard.GetData()). The source code of Clipboard.GetImage() is this:

public static Image GetImage() {
    var dataObject = Clipboard.GetDataObject();
    if (dataObject != null) {
        return dataObject.GetData(DataFormats.Bitmap, true) as Image;
    }

    return null;
}

Notice that your stack trace says that the exception happened in GetData(). Looking at the source code, that means that the call to GetDataObject() worked, which means you could (theoretically) use GetDataObject() yourself and convert the IDataObject from that into something you can use.

It might take some exploring to figure out what's going on. You might be able to use IDataObject.GetFormats() to inspect what it is, then use IDataObject.GetData() to get the data in that format.

Update 2: The solution from here points us in the right direction, but needs some modification:

var data = Clipboard.GetDataObject();
var ms = (MemoryStream) data.GetData("PNG+Office Art");

var image = Image.FromStream(ms)
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • It's not my medical application, so I have no control over it. But I believe that the image is copied to the clipboard correctly because I can paste it to Paint and Word. – redcurry Dec 19 '18 at 17:39
  • Well, if it's an image, 'adding attributes' is irrelevant since `Image` is a framework library. – Nyerguds Dec 19 '18 at 17:44
  • Well, that's the point. If this is happening, it wasn't likely a plain bitmap that was copied to the clipboard. I updated my answer with some details that might help explore what this application actually put on the clipboard. – Gabriel Luci Dec 19 '18 at 17:48
  • 1
    @redcurry Note that [there are many ways of copying an image to the clipboard](https://stackoverflow.com/a/46400011/395685)... and MS Office prefers a PNG MemoryStream object because classic windows image clipboard has no transparency support. You may need to dig deeper into what exactly is on that clipboard. – Nyerguds Dec 19 '18 at 17:52
  • @GabrielLuci You're right, GetDataObject() works, and when I obtain the formats, it includes "System.Drawing.Bitmap" and "System.Windows.Media.Imaging.BitmapSource". – redcurry Dec 19 '18 at 17:52
  • Does this give you anything useful?: `Clipboard.GetData("System.Drawing.Bitmap")` – Gabriel Luci Dec 19 '18 at 17:54
  • @GabrielLuci No, it gave me the same exception. I'm going to try every format available. – redcurry Dec 19 '18 at 17:58
  • Actually, don't use `Clipboard.GetData()`. Try using `IDataObject.GetData()`. I updated my answer with an example. There's actually an overload that takes a `Type`. It might work. – Gabriel Luci Dec 19 '18 at 18:03
  • You could also try telling it to not auto-convert the data by passing `false` as the [second parameter](https://learn.microsoft.com/en-us/dotnet/api/system.windows.idataobject.getdata?view=netframework-4.7.2#System_Windows_IDataObject_GetData_System_String_System_Boolean_): `dataObject.GetData("System.Drawing.Bitmap", false)` – Gabriel Luci Dec 19 '18 at 18:06
  • @GabrielLuci If it doesn't auto-convert, what would I get back? – redcurry Dec 19 '18 at 18:07
  • Probably just an `object`, but at least you can debug and inspect that and see what it is. – Gabriel Luci Dec 19 '18 at 18:08
  • @GabrielLuci After trying every possibly format, the ones where I actually got an object back were "PNG + Office Art", "Office Drawing Shape Format", "Format17", and "Enhanced Metafile". Now I need to figure out how to convert any of these to a Bitmap I can use. – redcurry Dec 19 '18 at 18:14
  • 2
    See here: https://stackoverflow.com/a/1406304/1202807 (use a `MemoryStream`) Seems @Nyerguds was right about it being a weird PNG MemoryStream. – Gabriel Luci Dec 19 '18 at 18:22
  • @GabrielLuci Trying "PNG" didn't work, but I'm getting a `System.Drawing.Imaging.Metafile` when I try an "EnhancedMetafile". The object appears to be a correct image because it has a width and height and other properties (while investigating with the Debugger). I just need to convert it to a Bitmap (since that's what I need). – redcurry Dec 19 '18 at 19:03
  • That answer is 9 years old, so maybe things have changed. Try it with `data.GetData("PNG+Office Art")` and see if that can be casted to a `MemoryStream` (I put that code in my answer). – Gabriel Luci Dec 19 '18 at 19:05
  • @GabrielLuci Sorry, I just saw you updated your answer. I tried it and that worked! The `Image.FromStream()` actually returns a `Bitmap`, which is what I need, so I don't even need to convert it. Thanks! – redcurry Dec 19 '18 at 19:07
  • 1
    Huh, seems I linked to the wrong post... [the one in which I detailed multiple clipboard formats](https://stackoverflow.com/a/46424800/395685) is actually mentioned in a comment on that answer. By the way, `Bitmap` is the standard implementation of the abstract `Image` class, so all that means is that the `MemoryStream` contained the bytes for a valid image file. If you have some WPF-specific way to open these bytes as image it might be simpler to do that directly. – Nyerguds Dec 20 '18 at 11:04