在 Windows 10 上使用 Clipboard.GetImage() 后屏幕截图中的文本被删除了?

Text erased from screenshot after using Clipboard.GetImage() on Windows 10?

这很奇怪: 我最近将我的工作站从 Windows 7 升级到 Windows 10。我有一个聊天客户端,它使用以下代码从剪贴板接受图像:

if (Clipboard.ContainsImage())
{
        BitmapSource source = Clipboard.GetImage();
        BitmapFrame frame = BitmapFrame.Create(source);
        var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
        encoder.Frames.Add(frame);
        var stream = new MemoryStream();
        encoder.Save(stream);
        byte[] daten = stream.ToArray();
        if (daten != null && daten.Length > 0)
        {
            sendFile(DateTime.Now.ToString("yyyyMMddHHmmss_") + "clipboard.png", stream.ToArray());
        }
}

这是我截图的区域应该的样子(例如,如果我将它粘贴到 MS-Paint 或直接从截图工具保存):

下面是我使用 Clipboard.GetImage(); 导入屏幕截图后的样子。

如您所见,所有文本都被擦除,如果您仔细观察,您会发现通常为白色的背景现在是透明的。

如果我使用 JpegBitmapEncoder 而不是 PngBitmapEncoder,它工作正常,所以这应该是一个编码问题,但让我感到困惑的是:

  1. 这在 Windows 7 上从未发生过 - 在 Windows 10 上发生了什么变化 这会使屏幕截图有任何不同吗?
  2. 如果我使用截图工具将屏幕截图保存到文件中,则会创建一个 PNG(数据本身带有 PNG-Header)。那么为什么 PNG 不是正确的编码?

我一直在研究 windows 剪贴板,我想我知道发生了什么。请耐心等待,这是一个很长的解释,并且需要进行大量研究才能获得有关处于混乱核心的 DIB 格式的足够信息。

默认情况下,出于遗留原因,剪贴板根本不支持透明度;回到最初设计剪贴板时,还没有 alpha 通道这样的东西。不过你可能知道,多种格式可以一起放在剪贴板上,让读取剪贴板的程序有更大范围的数据取出可能性,而且似乎在最近的 Windows 版本中,图像显然也以 DIB(设备独立位图)格式放在剪贴板上。现在,.Net framework v3.5 通常从剪贴板中获取标准 non-transparent "Image" 格式版本,因此它永远不会提供透明度,但似乎 4.5 可能实际上会得到这个 DIB。

存在于剪贴板上的 DIB 在技术上是每像素 32 位的 RGB 图像。注意,RGB,不是 ARGB。这意味着每个像素有四个字节,但是红绿蓝字节正常填充,第四个字节实际上是未定义的,不应指望实际表示"alpha"。它只是为了让数据很好地对齐到四个字节的倍数。

这个DIB的内部格式设置为32位"BITFIELDS",与标准的32位"RGB"类型不同的是,它在header中具体定义了用于每种颜色的每个 32 位像素的位数。但是它只定义了三个这样的位域;用于红色、绿色和蓝色。没有第四个字段;它不应该有阿尔法。考虑一下,如果该图像是由一个系统创建的,该系统确实只是将其视为没有 alpha 的 RGB,并且不会事先清除其内存(因为很多 C/C++ 系统不会),并且实际上只写入那些 R、G 和 B 字节,那么第四个字节可能只是之前操作留在内存中的随机垃圾。您甚至不能确定它被清除为值 0 或设置为标准不透明值 255。

还有问题...因为很多应用程序,显然包括 Windows 10 和 .Net framework 4.5,do 将其视为那。当我在由 2 个不同高度的屏幕组成的 Windows 10 桌面上按下 Print Screen 时,生成的图像实际上是由 Windows 本身放入剪贴板的 bastard-BITFIELDS-DIB 格式,其中包含透明度;当我使用我的功能转储图像以将该 DIB 保存为 ARGB 图像时,图像上落在我实际显示器之外的区域确实是 透明 ,而不仅仅是黑色;它是唯一一张 'alpha' 字节设置为 0 的图像。所以这似乎表明 Windows 10 本身也考虑了格式 alpha-capable.

所以我们这里的问题似乎是 Microsoft 本身没有正确使用自己的规范的问题。 (如果你想知道,是的,他们 制定了 DIB 规格。)他们完全可以切换到更新的 DIB 格式,header,确实 支持真正的 alpha,但他们使用一种混蛋旧的 RGB 格式,显然每个人(我在从 Google Chrome 复制图像时发现它)只是 假设 包含 alpha.

反过来,这似乎会导致像您一样的奇怪行为。我怀疑 Explorer window 的绘图机制中的某些东西用某些东西填充了这些 alpha 字节,甚至可能是它自己的绘图层之一的 alpha,并且在将最终图像放在剪贴板上时它永远不会被清除。然后,正如您所注意到的,.Net Framework 4.5 显然假定这些额外的字节确实是您获得的图像的 alpha 通道。

解决方案是将这些字节清除为 255,或者让您的系统将结果解释为 RGB 而不是 ARGB。我实际上可以帮助您的第二个:我在 GetImageData 函数中详细说明了如何从图像数据中获取字节 in this answer, and this one 有我的 BuildImage 方法将字节写回新图像。因此,如果您只是使用它来获取 32 位 ARGB 数据,然后将其写回新的 32 位 RGB 图像,则可以使框架将图像数据视为非透明数据,而无需修改单个字节。