使用 PngBitmapEncoder 的 RenderTargetBitmap 在没有显示时在虚拟机上生成空白图像,Windows 10 版本 1903

问题描述 投票:0回答:3

我们的 .NET 应用程序能够生成 .png 文件,这些文件是 WPF Canvas 的屏幕截图。

这可以从客户端触发并由我们的另一个应用程序处理(本质上是将作业分派给它进行排队和处理的应用程序),该应用程序可以在另一台计算机上运行,然后负责截取屏幕截图。

这个功能效果很好,并且已经使用了很长时间。这包括在虚拟机上,即使没有人连接到该机器,即没有渲染显示。我们没有对代码进行任何更改。

但是,从 Windows 10 版本 1903(2019 年 5 月更新)开始,如果在无人连接的虚拟机上生成,则生成的屏幕截图始终为空白。如果您当前已连接到虚拟机,则它可以正常工作。我们还在更新 1909 和 2004 中重现了此问题。

我们得到的结果是尺寸正确的完全透明的.png。

我已经远程调试了创建屏幕截图的虚拟机,没有任何明显错误 - 所有属性(例如高度、宽度和可见性)都是正确的。没有抛出异常。

我们认为这一定与没有可以访问的显示或者类似的东西有关。然而,奇怪的是,这过去工作得很好,所以我们对 Windows 更新之间可能发生的变化感到困惑,尽管我们的代码保持不变。

还有其他人遇到过这个问题或类似问题并设法解决它吗?我知道 WPF 不完全支持在服务中使用 - 这个用例是否会交叉到该用例,因为应用程序在技术上在创建渲染时没有显示?

这是我们代码的精简示例,供参考:

' 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
.net wpf vb.net windows-10 rendertargetbitmap
3个回答
3
投票

我们找出了问题所在。

Microsoft 于 2017 年修复了 Windows 问题,在此过程中,发布了默认行为,其中名为

ShouldRenderEvenWhenNoDisplayDevicesAreAvailable
的设置将设置为 False。显然,这意味着在我们的用例中,WPF 没有渲染任何内容,因为技术上没有可用的显示设备。

将以下内容添加到我们应用程序的 app.config 文件中可以修复该问题:

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

1
投票

我们的一个应用程序也有同样的问题。正如丹尼尔正确指出的那样,我们可以追溯到我们更新环境以获取此 Microsoft 更新时的根本原因。

我们的应用程序使用 RenderTargetBitmap 来生成图像。只要存在活动的远程桌面会话,它就会正确呈现,因为在这种情况下它会检测到有效的显示设备。但是,每当没有活动的远程桌面会话时,就会出现渲染问题,因为它无法检测到任何显示设备。因此,结果将是空白/透明图像。

Daniel 更新 App.Config 的答案非常适合我们的场景。但我们还发现微软有更多这方面的文档,其中大部分在AppContext下有详细介绍。

总而言之,选择退出更新的主要方法有3种。这些是按照 Microsoft 注释的优先顺序排列的,其中一个将覆盖另一个:

(1) 使用 SetSwitch 方法以编程方式更改设置,如下所示:

AppContext.SetSwitch("System.Windows.Media.ShouldRenderEvenWhenNoDisplayDevicesAreAvailable", true);

AppContext.SetSwitch("Switch.System.Windows.Media.ShouldNotRenderInNonInteractiveWindowStation", false);

(2)更改App.Config文件:

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

(3)更改注册表:

“向 HKLM\SOFTWARE\Microsoft.NETFramework\AppContext 子项添加新的字符串值。将条目的名称设置为交换机的名称。将其值设置为以下选项之一:True、true、False 或假的。

在 64 位操作系统上,您还必须将相同的条目添加到 HKLM\SOFTWARE\Wow6432Node\Microsoft.NETFramework\AppContext 子项。

使用注册表定义AppContext开关具有机器范围;也就是说,它会影响机器上运行的每个应用程序。”

相关链接如下:

https://support.microsoft.com/en-gb/topic/quality-rollup-for-net-framework-4-6-4-6-1-4-6-2-and-4-7- for-windows-server-2012-kb-4043762-6b1e1b85-5385-bf62-059a-2183b62d7153

https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.rendertargetbitmap.render

https://learn.microsoft.com/en-us/dotnet/api/system.appcontext

https://learn.microsoft.com/en-us/dotnet/api/system.appcontext.setswitch


0
投票

可以通过代码来完成此操作,您必须对两者使用“Switch”AppContext.SetSwitch:

AppContext.SetSwitch("Switch.System.Windows.Media.ShouldNotRenderInNonInteractiveWindowStation", false); AppContext.SetSwitch("Switch.System.Windows.Media.ShouldRenderEvenWhenNoDisplayDevicesAreAvailable", true);

并且这必须在初始化主窗口之前完成! 适用于 .NET Framework 和 .NET 5++

© www.soinside.com 2019 - 2024. All rights reserved.