Search code examples
.netwpfvb.netwindows-10rendertargetbitmap

RenderTargetBitmap using a PngBitmapEncoder is generating blank images on a virtual machine when there is no display, Windows 10 Version 1903


Our .NET application has the ability to generate .png files which are screenshots of a WPF Canvas.

This can be triggered from a client and handled by another of our applications (essentially something that has jobs farmed out to it that are queued and processed) that can be left running on another machine, which is then responsible for taking the screenshot.

This functionality works great and has done for a long time. This includes on VMs, even when there is nobody connected to that machine, i.e. there is no display rendered. We have not made any changes to the code.

However, since Windows 10 Version 1903 (May 2019 Update), the generated screenshots are always blank if they are generated on a virtual machine that nobody is connected to. It works fine if you are currently connected to the VM. We have also reproduced this issue on Updates 1909 and 2004.

The result that we get is a fully transparent .png of the correct size.

I have remotely debugged the virtual machine creating the screenshots and there is nothing obviously incorrect - all properties like heights, widths and visibility are correct. There are no exceptions thrown.

We think this must be related to the fact that there is no display that can be accessed or something similar. However, it is odd that this used to work fine, so we are stumped as to what could have changed between Windows updates despite our code remaining unchanged.

Has anyone else experienced this issue or similar and managed to work around it? I know that WPF isn't fully supported to be used in a service - does this use case cross over into that as the application technically has no display at the point of creating the render?

Here is a condensed example of our code for reference:

' A method that ensures all contents of the canvas has been loaded in, then sets the canvases height and width based on its children, 
' ensuring that it is layed out fully in preperation for the screenshot
  theCanvas.UpdateSize()

Try

    Dim renderBitmap As New RenderTargetBitmap(CInt(theCanvas.Width), CInt(theCanvas.Height), 96.0, 96.0, PixelFormats.Pbgra32)
    renderBitmap.Render(theCanvas)

    Dim directoryPath as String = Path.GetDirectoryName(saveLocation)
    Directory.CreateDirectory(directoryPath)

    Using outStream as New FileStream(saveLocation, FileMode.OpenOrCreate)

        Dim encoder As New PngBitmapEncoder()
        encoder.Frames.Add(BitmapFrame.Create(renderBitmap))

        encoder.Save(outStream)
    End Using

Catch ex as IOException
    ...
End Try

Solution

  • We figured out what the problem was.

    Microsoft fixed a Windows issue in 2017 and in doing so, shipped out default behavior where a setting called ShouldRenderEvenWhenNoDisplayDevicesAreAvailable would be set to False. Obviously this means that in our use case, WPF was not rendering anything as there was technically no display device available.

    Adding the below to the app.config file of our application fixes the issue:

    <runtime>
          <AppContextSwitchOverrides value="Switch.System.Windows.Media.ShouldNotRenderInNonInteractiveWindowStation=false;Switch.System.Windows.Media.ShouldRenderEvenWhenNoDisplayDevicesAreAvailable=true" />
    </runtime>