5

I'm trying to grab a screenshot and save it on disk using Xamarin and C# on Mac. I wrote the code below:

public static void TakeScreenshotAndSaveToDisk(string path)
    {
        var fullScreenBounds = NSScreen.MainScreen.Frame;
        IntPtr ptr = CGWindowListCreateImage(fullScreenBounds, CGWindowListOption.OnScreenAboveWindow, 0, CGWindowImageOption.Default);
        var cgImage = new CGImage(ptr);
        var fileURL = new NSUrl(path, false);
        var imageDestination = CGImageDestination.Create(new CGDataConsumer(fileURL), UTType.PNG, 1);
        imageDestination.AddImage(cgImage);
        imageDestination.Close();
        imageDestination.Dispose();
        fileURL.Dispose();
        cgImage.Dispose();
    }

The method executes and the file appears at the correct location. If I try to open it, it will show blank. If I click "Get Info" on the file it will not show a preview. After I close my app, the image can be opened and "Get Info" shows the preview.

What am I doing wrong here? It seems to me that the resources are not released even though I call Dispose() on the objects.

Thanks.

pinedax
  • 9,246
  • 2
  • 23
  • 30
BVintila
  • 153
  • 5

2 Answers2

3

The CGImageDestination.Create method has 3 different signatures, if you use the one that accepts a NSUrl instead of a CGDataConsumer you should be good.

var imageDestination = CGImageDestination.Create(fileURL, UTType.PNG, 1);

With this one you don't need to create a CGDataConsumer but if you really want/need to

var dataConsumer = new CGDataConsumer(fileURL);
var imageDestination = CGImageDestination.Create(dataConsumer, UTType.PNG, 1);
imageDestination.AddImage(cgImage);
imageDestination.Close();
dataConsumer.Dispose();

Just make sure to dispose the instance once you have saved the file.

With the using approach:

using (var dataConsumer = new CGDataConsumer(fileURL))
{
    var imageDestination = CGImageDestination.Create(dataConsumer, UTType.PNG, 1);
    imageDestination.AddImage(cgImage);
    imageDestination.Close();
}

Note: for the CGImageDestination you don't need to manually dispose, the Close method will also dispose the object (based on the documentation).

public Boolean Close ()

Writes the images to the destination and disposes the object.

Hope this helps.-

pinedax
  • 9,246
  • 2
  • 23
  • 30
1

I may be late into this but have just implemented this Swift algorithm with Xamarin.Mac, and can be consumed in a head-less mode too:

https://gist.github.com/MarcosCobena/b4768bacc1a112a4f38a9d11a19f1251

It relies on "new" CoreGraphics bindings (some were already present in Xamarin.Mac, but internal or private) to detect displays and enumerate them. Finally, it takes one screenshot per display, saving it as PNG in a given path.

  • 1
    Just linking to your own library or tutorial is not a good answer. Linking to it, explaining why it solves the problem, providing code on how to do so and disclaiming that you wrote it makes for a better answer. See: [What signifies “Good” self promotion?](https://meta.stackexchange.com/q/182212) – 4b0 Aug 28 '18 at 11:00