Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin - How to get a screenshot and save it on disk using xamarin mac?

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.

like image 823
BVintila Avatar asked Aug 16 '17 13:08

BVintila


2 Answers

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.-

like image 187
pinedax Avatar answered Oct 23 '22 03:10

pinedax


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.

like image 31
Marcos Cobeña Morián Avatar answered Oct 23 '22 03:10

Marcos Cobeña Morián