Delphi 11中SetWindowDisplayAffinity使用异常问题求助
问题分析与修正:Delphi中SetWindowDisplayAffinity无法阻止截图的问题
问题背景
我之前在提问寻求Delphi Alexandria中阻止截图的方案,找到一段代码测试后,无法实现阻止页面被捕获的效果,也无法让截图时页面变为黑屏。我仅希望学习SetWindowDisplayAffinity的正确用法,并非开发安全软件,清楚该方法存在绕过方式,且需要兼容Windows 7至11系统(此前尝试DirectX SDK方案未成功)。
测试代码如下:
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.Menus, Vcl.ExtCtrls, Vcl.FileCtrl, System.IOUtils, System.Types, System.UITypes, Forms, Math, ShellAPI, Vcl.Imaging.jpeg, Winapi.TlHelp32, PsAPI, System.StrUtils; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} uses Unit2; function SetWindowDisplayAffinity(hWnd: HWND; dwAffinity: DWORD): BOOL; stdcall; external 'user32.dll' name 'SetWindowDisplayAffinity'; function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall; external 'user32.dll' name 'ShowWindow'; procedure TForm1.FormCreate(Sender: TObject); var Handle: Winapi.Windows.HWND; begin // Get the handle of the main form window Handle := Self.Handle; // Set the display affinity of the window to monitor SetWindowDisplayAffinity(Handle, WDA_MONITOR); // Call SetWindowDisplayAffinity if not SetWindowDisplayAffinity(Handle, WDA_MONITOR) then begin ShowMessage(SysErrorMessage(GetLastError)); end else begin ShowMessage('OK'); end; // Show the window ShowWindow(Handle, SW_NORMAL); end;
代码存在的问题
- API兼容性限制:
SetWindowDisplayAffinity是Windows 8.1及以上系统才提供的API,Windows 7完全不支持,这是该代码在Win7上无效的核心原因。 - 常量未定义:代码中直接使用
WDA_MONITOR,但Delphi的Winapi.Windows单元可能未包含该常量定义(尤其是针对低版本Windows编译时),导致实际传递的参数值错误,API调用失效。 - 重复调用API:连续两次调用
SetWindowDisplayAffinity,第一次无错误检查,属于冗余操作,可能干扰后续执行。 - 调用时机不当:在
FormCreate中调用时,窗口尚未完全初始化完成,此时设置窗口属性可能无法生效。 - 冗余的ShowWindow调用:VCL框架会自动处理窗口显示逻辑,额外调用
ShowWindow(Handle, SW_NORMAL)无意义,甚至可能引发异常。
修正后的代码
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) procedure FormShow(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; // 手动定义SetWindowDisplayAffinity所需常量 const WDA_MONITOR = $00000001; // 仅允许在显示器显示,禁止截图捕获 WDA_NONE = $00000000; // 取消限制 implementation {$R *.dfm} // 声明API(仅Windows 8.1+支持) function SetWindowDisplayAffinity(hWnd: HWND; dwAffinity: DWORD): BOOL; stdcall; external 'user32.dll' name 'SetWindowDisplayAffinity'; procedure TForm1.FormShow(Sender: TObject); begin // 在窗口显示后设置属性 if not SetWindowDisplayAffinity(Self.Handle, WDA_MONITOR) then begin ShowMessage('设置失败: ' + SysErrorMessage(GetLastError)); end else begin ShowMessage('设置成功,窗口已禁止常规截图捕获'); end; end; end.
额外说明
- 若需兼容Windows 7,
SetWindowDisplayAffinity无法使用,需采用其他方案(如GDI消息拦截或DirectX渲染保护),但这类实现复杂度较高。 - 该API仅能阻止常规截图工具(如PrintScreen、系统截图工具),无法防范摄像头拍摄、虚拟机截图等绕过方式。
内容的提问来源于Stack Exchange,提问作者John
相关产品推荐
相关产品推荐

