WDA_EXCLUDEFROMCAPTURE失效?原因及解决方法
2024-12-31 06:42:28
WDA_EXCLUDEFROMCAPTURE 失效:行为如同 WDA_MONITOR 的原因及解决
在Windows环境下,开发者有时需要控制窗口是否被屏幕截图捕获。SetWindowDisplayAffinity
API 提供这种能力, 其中 WDA_EXCLUDEFROMCAPTURE
常被期望隐藏窗口内容,使其不出现在截图里。 然而,实际应用中可能遇到 WDA_EXCLUDEFROMCAPTURE
行为像 WDA_MONITOR
(窗口内容显示为黑色) 的情况。 这篇文章将深入探讨这类问题的原因,并提供切实可行的解决方案。
问题的根源:层叠窗口的交互影响
WDA_EXCLUDEFROMCAPTURE
期望阻止窗口出现在截图或其他捕获机制里,而 WDA_MONITOR
确实能将窗口内容渲染成黑色, 达到内容保护的目的。当设置窗口的 WS_EX_LAYERED
样式时,通常是需要实现透明或半透明效果,这会引发与 WDA_EXCLUDEFROMCAPTURE
之间的冲突。 根本原因在于系统如何处理带有 WS_EX_LAYERED
样式和 WDA_EXCLUDEFROMCAPTURE
属性的窗口截图。
层叠窗口需要特殊的渲染处理, 它们并不是传统的窗口绘制,而是以复合方式呈现。 当窗口既是层叠窗口又是尝试排除截图时,系统处理逻辑可能出现偏差。 它会将排除捕获的行为误解成将整个窗口都视为受保护区域,从而变成类似 WDA_MONITOR
的效果,使窗口显示为黑色。
解决方案
以下几种方法可以帮助解决这个问题,使 WDA_EXCLUDEFROMCAPTURE
正常工作:
方案一:移除 Layered 样式
如果窗口的透明不是必需的,最直接的方案是移除 WS_EX_LAYERED
样式。通过此调整,窗口将以标准方式绘制, 并配合 WDA_EXCLUDEFROMCAPTURE
使用。这将能够避免不正常的显示行为。
代码示例:
import tkinter as tk
import ctypes
from ctypes import wintypes
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
WS_EX_TRANSPARENT = 0x00000020
LWA_COLORKEY = 0x00000001
LWA_ALPHA = 0x00000002
def apply_exclude_capture(root):
user32 = ctypes.WinDLL('user32', use_last_error=True)
user32.SetWindowDisplayAffinity.argtypes = wintypes.HWND, wintypes.DWORD
user32.SetWindowDisplayAffinity.restype = wintypes.BOOL
WDA_EXCLUDEFROMCAPTURE = 0x00000011
result = user32.SetWindowDisplayAffinity(root.winfo_id(), WDA_EXCLUDEFROMCAPTURE)
if not result:
raise ctypes.WinError(ctypes.get_last_error())
else:
print("SetWindowDisplayAffinity applied with exclusion!")
root = tk.Tk()
root.title("App")
root.geometry('1500x750')
root.resizable(False, False)
canvas = tk.Canvas(root, width=1500, height=750, bg='white', highlightthickness=0)
canvas.pack()
apply_exclude_capture(root)
# 将层叠属性移除
hwnd = canvas.winfo_id()
styles = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
styles &= ~(WS_EX_LAYERED | WS_EX_TRANSPARENT)
ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, styles)
#不再需要使用透明相关的设置
# SetLayeredWindowAttributes(hwnd, 16777215, 255, LWA_COLORKEY | LWA_ALPHA)
root.mainloop()
操作步骤:
- 创建一个基础的
tkinter
窗口。 - 注释掉原有的设置
WS_EX_LAYERED
、WS_EX_TRANSPARENT
样式以及透明属性设置代码,或者,也可以删除,确保窗口不再是层叠窗口。 - 使用
SetWindowDisplayAffinity
应用WDA_EXCLUDEFROMCAPTURE
。
方案二:使用 SetLayeredWindowAttributes 进行色彩键设置
如果需要保持透明, SetLayeredWindowAttributes
和颜色键是一个备选项。 可以通过创建一个指定颜色(通常是纯色)的透明窗口来实现。
WDA_EXCLUDEFROMCAPTURE
在此设置下可能可以按照预期工作,不过实际表现因系统和驱动而异。
代码示例:
import tkinter as tk
import ctypes
from ctypes import wintypes
GWL_EXSTYLE = -20
WS_EX_LAYERED = 0x00080000
WS_EX_TRANSPARENT = 0x00000020
LWA_COLORKEY = 0x00000001
LWA_ALPHA = 0x00000002
def apply_exclude_capture(root):
user32 = ctypes.WinDLL('user32', use_last_error=True)
user32.SetWindowDisplayAffinity.argtypes = wintypes.HWND, wintypes.DWORD
user32.SetWindowDisplayAffinity.restype = wintypes.BOOL
WDA_EXCLUDEFROMCAPTURE = 0x00000011
result = user32.SetWindowDisplayAffinity(root.winfo_id(), WDA_EXCLUDEFROMCAPTURE)
if not result:
raise ctypes.WinError(ctypes.get_last_error())
else:
print("SetWindowDisplayAffinity applied with exclusion!")
root = tk.Tk()
root.title("App")
root.geometry('1500x750')
root.resizable(False, False)
canvas = tk.Canvas(root, width=1500, height=750, bg='red', highlightthickness=0)
canvas.pack()
apply_exclude_capture(root)
hwnd = canvas.winfo_id()
styles = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
styles |= WS_EX_LAYERED | WS_EX_TRANSPARENT
ctypes.windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, styles)
#设置color key
SetLayeredWindowAttributes = ctypes.windll.user32.SetLayeredWindowAttributes
SetLayeredWindowAttributes.argtypes = [wintypes.HWND, wintypes.COLORREF, wintypes.BYTE, wintypes.DWORD]
SetLayeredWindowAttributes.restype = wintypes.BOOL
SetLayeredWindowAttributes(hwnd, 0x000000ff, 0, LWA_COLORKEY)
root.mainloop()
操作步骤:
- 设置
WS_EX_LAYERED
和WS_EX_TRANSPARENT
窗口扩展样式。 - 创建带有纯色背景(如红色)的 Canvas。
- 调用
SetLayeredWindowAttributes
使用颜色键将指定背景色透明。 - 必须在设置 Layered 样式之后 应用
SetWindowDisplayAffinity
。
方案三:使用 DWM 复合(需要谨慎测试)
Direct Composition Manager (DWM) 也影响窗口如何捕获。可以尝试禁用或调整DWM的窗口合成, 但这种操作可能影响其他程序的视觉呈现效果,需小心使用并测试,以保证应用兼容性。 这项设置可以考虑通过配置应用的方式实现,而不修改用户全局系统设置,但操作上相对比较复杂。具体方案因操作系统和实际需求而异。
操作步骤
此方案较为复杂,并不适合直接提供代码,以下仅做说明:
- 可以使用Windows提供的DWM API对窗口进行特定设置。
- 利用
DwmSetWindowAttribute
可以调整窗口的合成方式。 - 验证这种方法是否会影响WDA_EXCLUDEFROMCAPTURE。
重要提示: 此方案可能会导致其他视觉问题,建议在充分测试和理解DWM影响的情况下采用。
安全提示
在实际应用 WDA_EXCLUDEFROMCAPTURE
时,需谨慎处理窗口内容保护,防止安全风险:
- 验证所使用的API版本,确保在目标系统上能按预期运行。
- 除了截图捕获,也要注意其他的捕获方式,例如录屏,摄像头等。
- 充分测试各个解决方案,找到与自己应用场景最为匹配的方案。
总之, 解决 WDA_EXCLUDEFROMCAPTURE
行为异常问题的关键是理解层叠窗口的机制,并且明确需要使用的窗口特性。 选择合适的方案能有效的实现预期结果,并在最大程度上规避安全隐患。