【问题标题】:Why this BitBlt example doesn't work anymore?为什么这个 BitBlt 示例不再起作用?
【发布时间】:2018-06-12 14:43:14
【问题描述】:

我目前正在使用 Petzold 的书(第 5 版)回到一些 Windows 编程。 我使用 BitBlt 编译了following example,但它没有按预期工作。

它应该复制(CxSource,CySource)大小的窗口图标并将其复制到整个窗口的表面上。 实际上,使用 Windows 7 会发生什么情况是窗口下方的位图被获取并复制到绘图表面,即 hdcClient。

知道传递给 BitBlt 的 DC 很明显是 hdcWindow,我不明白它为什么会这样,它指的是通过当前应用程序的 GetWindowDC(hwnd) 获得的设备上下文。

我首先认为这是由于默认情况下启用了透明模式,但禁用它并不会改变任何内容。 BitBlt 似乎总是在应用程序窗口下方出现! 我不明白! :) 任何人都知道它为什么会这样工作以及如何解决它?

【问题讨论】:

  • GetWindowDC 应该仍然可以工作,因为它是 Win32 API (user32.lib) 的一部分。
  • 请不要发布外部链接。在您的问题中插入代码。
  • 嗯 ... e̶a̶t̶i̶n̶g̶ 删除我以前的 cmets。也许我需要看到这个结果。听起来好像cxSourcecySource 指向窗口的错误部分。仍然有可能“框架厚度 + 默认大小”不再是获取窗口图标的可靠方法,但是 MSDN 上似乎没有提到这种方法或替代方法。
  • @BarmakShemirani:关于非链接,我一般同意你的看法,但在这种特殊情况下,提到专门为 Petzold 书创建的带有代码示例的 github 页面对我来说看起来完全没问题。
  • @BarmakShemirani 对我来说,从 Petzold 的 SO 书籍中发布源代码是否可以被视为“合理使用”,正如他在 FAQ 中所写的那样,这还不是很清楚。所以安全总比后悔好。

标签: c windows winapi


【解决方案1】:

自从添加了 DWM(桌面窗口管理器,又名 Aero)以来,使用 BitBlt() 制作屏幕截图并没有变得更容易。 Petzold 的示例代码存在一个微妙的时间问题,它使屏幕截图过早。它在 Aero 仍在忙于为帧设置动画时执行此操作,使其淡入视野。因此,您可以看到窗口后面的内容,可能已经部分褪色,具体取决于生成第一条 WM_PAINT 消息的速度。

您可以通过禁用效果轻松修复它:

#include <windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

在 CreateWindow() 调用之后:

BOOL disabled = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disabled, sizeof(disabled));

另一个棘手的细节是第一个 BitBlt 很重要,DWM 之后会返回一个缓存副本,该副本没有被动画正确无效。

当您需要属于另一个进程的窗口的屏幕截图时,这会变得更加困难。但这在 Aero 之前就已经是一个问题了,您必须等待足够长的时间才能确保窗户完全涂漆。值得注意的也许是 BitBlt() 的性能,由于不得不从窗口后台缓冲区合成最终图像,它明显陷入困境。在 SO 有很多关于此的问题,但没有满意的答案。

【讨论】:

  • 谢谢汉斯。你是对的。添加 DwmSetWindowAttribute 允许复制位于标题栏中的图标。代码实际上相当陈旧,甚至令人惊讶的是,我在书中尝试的许多示例即使在最新的 Windows 10 上仍然兼容。
【解决方案2】:

不是要复制windows图标,是要复制图标所在的windows标题栏部分。

这有一些问题(现在有 20 年的旧代码):

  • GetSystemMetrics 值不能再用于窗口相关尺寸,因为 GetSystemMetrics 返回经典尺寸,而不是视觉样式尺寸。

  • 根据 Windows 版本,DWM 可能会将窗口大小定义为大于您的窗口(在该窗口中绘制窗口阴影和其他效果)。

您的示例在 XP 上运行良好:

(有一个小问题,因为标题栏不是方形的(与此示例设计的 Windows 98/2000 不同)所以您会在左上角看到一个问题,它只是白色的。我还稍微修改了这个示例,所以它会改变 HDC 源位置)

在现代版本的 Windows 上,DWM 或其他东西似乎无法正确模拟简单的窗口 DC,并且部分阴影/边框/效果区域是 DC 的一部分:

我不知道如何解决这个问题,但无论如何这个例子都没什么用,如果你想绘制窗口图标,你应该使用DrawIconEx 绘制 HICON。如果你想绘制自定义的非客户区的东西,那么你需要找到更多最近的例子,而不是只支持经典主题的东西。

【讨论】:

  • ? 干得好!但现在我不知道如何称呼这个。 OP 在他的假设中是错误的,即 BitBlt“没有按预期的方式工作”——它仍然可以。示例代码100% 完成了它应该做的事情(在整个客户区复制窗口的一个相当随机的部分)。
  • 我确实看到了 OP 谈到的一些“窗口下方的位图获取源”效果,但很难诊断发生了什么。
  • 感谢安德斯的评论。我没有正确表达自己,但实际上应该是复制标题栏中的小图标。正如 Hans 所解释的,在特定窗口上禁用 Aero 可以解决问题并允许获得预期的行为。我的意思是确保我正确理解 BitBlt 的工作原理。感谢您分享所有这些想法。
  • 是的,如果你仔细看我的例子,你会发现它有点透明,是一些快速动画的一部分。 DwmSetWindowAttribute 确实完全解决了这个问题。
猜你喜欢
  • 1970-01-01
  • 2011-04-10
  • 1970-01-01
  • 2019-04-07
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多