Search code examples
c#macosxamarinscreenshotxamarin.mac

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.


Solution

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