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

这是一个奇怪的问题:我最近将我的工作站从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();导入屏幕截图后,现在看起来就像这样Clipboard.GetImage();使用Glipboard.GetImage()后的屏幕截图

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

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

  1. 这在Windows 7上从未发生过 – Windows 10中的哪些内容可能会使屏幕截图有所不同?
  2. 如果我将截图保存到Snipping Tool中的文件,则会创建一个PNG(数据本身带有PNG-Header)。 那么为什么PNG不是正确的编码?

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

默认情况下,由于遗留原因,剪贴板根本不支持透明度; 当剪贴板最初设计时,没有像alpha通道这样的东西。 但是,您可能知道,可以将多种格式放在剪贴板上,以便为读取剪贴板的程序提供更广泛的数据输出可能性,而且在最近的Windows版本中,图像显然也会被放在DIB(设备无关位图)格式的剪贴板。 现在,.Net框架v3.5通常从剪贴板中采用标准的非透明“图像”格式版本,因此它永远不会提供透明度,但似乎4.5实际上可能会获得这个DIB。

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

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

而且存在问题……因为很多应用程序,包括显然是Windows 10和.Net framework 4.5, 都会这样对待它。 当我在由具有不同高度的2个屏幕组成的Windows 10桌面上按下Print Screen时,生成的图像实际上被Windows本身放在剪贴板中,因为它具有透明性的bastard-BITFIELDS-DIB格式; 当我用我的function转储图像以将DIB保存为ARGB图像时,图像上落在实际监视器外面的区域确实是透明的 ,而不仅仅是黑色; 它是图像的唯一一部分,其’alpha’字节设置为0.所以这似乎表明Windows 10本身也考虑了具有alphafunction的格式。

所以我们这里所遇到的问题似乎是微软本身没有正确使用自己的规范。 (如果你想知道,是的, 他们制定了DIB规范。)他们可以完全切换到更新的DIB格式,带有更大的标题,它确实支持真正的alpha,但是他们使用了一个卑鄙的旧RGB格式,显然每个人(我在从谷歌浏览器复制图像时发现它)只是假设包含alpha。

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

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