Search code examples
c#bitmapscreenshotwinui-3

WinUI3 BitmapEncoder.FlushAsync() hangs


I have the following code in my WinUI3 app:

public class Screenshot(byte[] pixels, int width, int height)
{
    public byte[] Pixels { get; } = pixels;
    public int Width { get; } = width;
    public int Height { get; } = height;
}

public static async Task<string> ByteArrayToBase64PNGStringAsync(Screenshot screenshot)
{
    using (InMemoryRandomAccessStream memoryStream = new())
    {
        await RenderPixelsToRasterStreamAsync(memoryStream, screenshot);

        using (DataReader reader = new(memoryStream.GetInputStreamAt(0)))
        {
            await reader.LoadAsync((uint)memoryStream.Size);
            byte[] byteArray = new byte[memoryStream.Size];
            reader.ReadBytes(byteArray);

            return Convert.ToBase64String(byteArray);
        }                
    }     
}  

public static async Task<bool> CopyScreenshotToClipboardAsync(Screenshot screenshot)
{
    InMemoryRandomAccessStream stream = new();
    await RenderPixelsToRasterStreamAsync(stream, screenshot);

    DataPackage dataPackage = new();
    dataPackage.RequestedOperation = DataPackageOperation.Copy;
    dataPackage.SetBitmap(RandomAccessStreamReference.CreateFromStream(stream));

    try
    {
        Clipboard.SetContent(dataPackage);
        return true;
    }
    catch (Exception)
    {
        return false;
    }
    finally
    {
        Debug.WriteLine("Clipboard flushed");
        Clipboard.Flush();
        stream.Dispose();
    }
}

private static async Task RenderPixelsToRasterStreamAsync(IRandomAccessStream stream, Screenshot screenshot)
{
    double dpi = App.Window.DPI;
    Debug.WriteLine($"RenderPixelsToRasterStreamAsync method called with {(uint)screenshot.Width} {(uint)screenshot.Height} {dpi}");
    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
    Debug.WriteLine("Encoder initialised");
    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)screenshot.Width, (uint)screenshot.Height, dpi, dpi, screenshot.Pixels);
    Debug.WriteLine("Bitmap set");
    await encoder.FlushAsync();
    Debug.WriteLine("Encoder flushed");
}

I can call any combination/repetitions of ByteArrayToBase64PNGStringAsync and CopyScreenshotToClipboardAsync without issue, apart from if I call CopyScreenshotToClipboardAsync then ByteArrayToBase64PNGStringAsync, in which case the code hangs indefinitely on await encoder.FlushAsync(). (using the UI thread throughout).

Can anyone suggest what might be causing this?


Solution

  • Whilst trying to make a reproduceable example I discovered a .GetAwaiter().GetResult() back in the calling code that seemed to be creating a deadlock only in the scenario I described in my question. Replacing this with an async function solved the issue.