如何在窗口外绘制?

4
我将为您翻译编程相关内容。在查看 Windows 工具提示类提示窗口时,我发现它的阴影是绘制在提示窗口实际矩形框外部的。使用 SpyXX,我可以获取工具提示窗口的窗口矩形和类样式:
Rectangle:     (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect:   (0, 0)-(104, 20), 104x20

你会注意到,你看到的下拉阴影实际上是被绘制窗口外部的。我怎样能够在我的窗口外部绘制一个阴影呢? 注意: 这个阴影不是使用标准的CS_DROPSHADOW类样式来绘制的。 我已经通过实验证实了这一点,并且也可以在中看到窗口的类样式;它不使用CS_DROPSHADOW
Windows Styles:     94000001

    WS_POPUP        80000000
    WS_VISIBLE      10000000
    WS_CLIPSIBLINGS  4000000
    TTS_ALWAYSTIP          1

Extended Styles:    00080088

    WS_EX_LAYERED      80000
    WS_EX_TOOLWIN         80
    WS_EX_TOPMOST          8

我该如何在窗口外绘图?
注意:在桌面DC上绘制是不行的。根据Greg Schechter的 Redirecting GDI,DirectX和WPF应用程序所述:
画图和屏幕读写--非常糟糕!最后,由于我们正在谈论重定向主题,一种特别危险的做法是将内容写入屏幕,通过使用GetDC(NULL)并在其上书写,或尝试进行XOR橡皮筋线等操作。有两个重要原因使得在屏幕上写入内容变得糟糕:它很昂贵...写入屏幕本身并不昂贵,但它几乎总是伴随着从屏幕读取,因为当写入屏幕时,通常会执行类似于XOR的读取-修改-写入操作。从视频内存表面读取非常昂贵,需要与DWM同步,并阻塞整个GPU管道以及DWM应用程序管道。它是不可预测的...如果你设法到达实际的主屏幕并写入它,那么你所写入的内容能保持多长时间就无法预测了。由于UCE不知道这一点,它可能会在下一帧刷新中被清除,或者根据其他需要更新的屏幕内容而持续存在很长时间。(出于这个原因,我们实际上不允许直接写入主屏幕...例如,如果你尝试访问DirectDraw主屏幕,DWM将在访问应用程序退出之前关闭)

简单的答案是你不能在窗口外绘制。出于给出的原因,这是一个不好的想法。这意味着当窗口矩形外有阴影时,你看到的不是原始桌面 - 而是某种组合图像。在Windows 6上,组合桌面由dwm.exe管理 - 在Windows XP上我不知道 - 你是否使用DrawThemeBackground来绘制控件背景? - Chris Becke
3个回答

5

以您描述的方式无法在窗口外绘制。

如果您右键单击桌面,然后转到属性/外观/效果并取消选中“在菜单下显示阴影”... 您将不再拥有阴影。

底线是这是窗口管理器的产物,而不是您的程序。


tooltips 公共控件如何在其窗口外绘制?或者它要求使用 Windows 中的哪个功能? - Ian Boyd
@Ian Boyd - 我认为你误解了。控件本身并没有在其窗口外绘制。当窗口管理器被要求绘制某些类别的窗口,比如“菜单”或“工具提示”,它(窗口管理器)会先渲染阴影,然后再渲染窗口。这是内置于Windows默认窗口管理器中的。有一些可替换的窗口管理器可供选择。其中一些可能是开源的,并且揭示了它们的秘密。但是,没有任何一个窗口是“在自己之外绘制”的。 - user113476
工具提示常规控件如何向Windows窗口管理器请求在其窗口周围渲染一个投影效果? - Ian Boyd
底线是,这不可能是窗口管理器的技巧,因为窗口管理器没有被给予必要的提示来告诉它绘制下拉阴影。这意味着工具提示控件明确调用某种窗口管理器API来手动渲染下拉阴影。那么这个API是什么?我很感兴趣。 - Chris Becke
3
窗口管理器仅向窗口添加CS_DROPSHADOW样式。在您上面的帖子中,您没有列出类样式,只有窗口样式。如果您实际上枚举具有'在菜单下显示阴影'选项已选中的工具提示窗口上的类样式,则会看到CS_DROPSHADOW类样式。取消选择该选项后,您将看不到它。类样式与窗口样式不同。为什么不编写一个示例程序来测试呢?我现在没有时间。 - user113476
工具提示(可能)使用CS_DROPSHADOW类样式(我不知道如何获取hWnd的类样式)来添加阴影。阴影与窗口的非矩形形状匹配的原因是窗口应用了区域(GetWindowRgn确认此点)。因此,“您无法在窗口外绘制”是正确的答案。+1并接受。 - Ian Boyd

4

问题:如何在一个窗口之外绘制? 答案:在另一个窗口内绘制!

首先要注意的是,工具提示类实际上确实使用了CS_DROPSHADOW样式 - 但请注意,这是一个样式,而不是窗口样式,因此您必须查看Spy++属性对话框中的选项卡。您会发现tooltips_class32窗口确实有这个样式 - 还有其他一些样式。

但这只是引出了下一个问题 - 那个是如何工作的呢?嗯,似乎Windows通过创建一个辅助HWND来绘制阴影 - 可能是创建另一个与其相同大小和形状的弹出窗口,用灰色填充它,将其直接放置在主窗口下方,并将其设置为WS_EX_LAYERED窗口,以便阴影可以透明并通过alpha混合在边缘处淡出。如果您想向自己的窗口添加不同类型的阴影效果,则没有任何阻止您使用相同或类似的技术。

因此,长话短说:如果您想在自己的窗口外绘制,请在您要绘制的一般区域创建一个辅助透明窗口,并在该辅助窗口上绘制。

--


现在,如果你试图在Spy++中找到其中一个帮助器阴影窗口,你不会找到太多东西。与tooltip_class32窗口不同,后者是长期存在的,只在需要时隐藏/显示自己,这些阴影窗口是一种更为难以捉摸的生物:它们只在需要时创建,因此在使用阴影的工具提示或弹出菜单或其他窗口时,您必须在刷新Spy++时进行操作,而这很棘手,因为大多数工具提示和菜单将在您将鼠标移动到Spy++时消失。但事实证明,Spy++自己的工具栏上的工具提示将保留下来:因此启动Spy++,将鼠标悬停在工具栏中的项目上,然后按F5在工具提示和阴影存在的情况下刷新HWND树。现在向下滚动,您应该看到树中第三个和第四个可见的HWND分别是工具提示本身,以及在其后面的一个SysShadow窗口。不幸的是,由于工具提示和阴影现在已经消失,如果您尝试获取该HWND的属性对话框,您将得到一个带有“无效窗口”消息的空白属性对话框。如果您真的想四处探索并查看SysShadow如何工作,它本身使用的样式等等,您可以创建一个目标应用程序,其中包含使用CS_DROPSHADOW的长期弹出窗口,然后可以随意在Spy++中进行探索。
(最后,请注意,这些阴影与 Vista 以来在另一个应用程序窗口之上看到的阴影完全不同:这种类型的阴影是 Aero Glass 模式的一部分,并由相同的桌面组合管理器处理,该管理器添加玻璃标题栏效果,并且它不使用或需要辅助窗口来实现阴影。)

如果有人感兴趣的话,工具提示窗口似乎设置了WS_CLIPCHILDREN、WS_CLIPSIBLINGS、WS_POPUP/WS_POPUPWINDOW和WS_VISIBLE。这是从在Visual Studio中拦截SetWinEventHook事件的一些代码中获取的,然后只需检查窗口上设置的样式即可。 - Martin B

1
我不会感到惊讶,如果那个阴影与窗口管理器本身密切相关;毕竟是窗口管理器决定哪个窗口可以绘制自己的哪些部分以及何时可以这样做。如果获得了对所有这些的控制权,窗口管理器就可以轻松地绘制那个阴影,我并不认为这是什么高深的技术。

我不相信tooltips公共控件会自行绘制下拉阴影。但是它又利用了 Windows 的哪个特性呢? - Ian Boyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接