摸鱼笔记[8]-OnTopReplica窗口克隆工具
摘要
在windows10上使用OnTopReplica将窗口画面复制到多个显示器上.
声明
本文内容由 AI 辅助生成, 已经人工审核和编辑。
简介
OnTopReplica简介
[https://github.com/LorenzCK/OnTopReplica]
OnTopReplica 是一款适用于 Windows Vista/7/8/10 的实时置顶窗口克隆工具。
启动后默认显示一个空白置顶窗口,用户可选择系统中任意其他窗口进行实时克隆,并保持始终置顶。非常适合监控后台进程、操作复杂多窗口游戏或工具、边工作边观看 YouTube 视频等场景。
📢 主要功能:
- 克隆任意窗口并保持始终置顶,不影响其他窗口操作
- 支持选择克隆窗口的子区域,可保存常用区域,或使用相对于目标窗口边框的相对坐标
- 自动缩放(适配原始窗口、1/2、1/4 及全屏模式)
- 屏幕角落位置锁定
- 可调节透明度
- 点击转发:允许与克隆窗口进行交互
- 穿透点击:忽略鼠标交互(配合部分透明度可将 OnTopReplica 作为悬浮覆盖层使用)
- 分组切换模式:自动在常用窗口组间切换
系统要求:
- Windows Vista 或更高版本(使用原生 DWM 缩略图技术实现克隆)
- Microsoft .NET Framework 4.7
- 开启桌面合成(即 Windows Aero 效果)
安装方式:
从 Releases 页面下载 MSI 安装包。
开源协议:
采用 MS-RL (Microsoft Reciprocal License) 许可证。欢迎提交 Issue 和反馈,也欢迎 Fork 贡献代码!
Windows DWM (Desktop Window Manager)简介
[https://learn.microsoft.com/en-us/windows/win32/dwm/dwm-overview]
Desktop Window Manager(DWM,桌面窗口管理器) 是微软 Windows 操作系统中的一个核心图形合成组件,最初随 Windows Vista 引入,作为桌面合成(Desktop Composition)功能的核心执行者。它从根本上改变了应用程序在屏幕上显示像素的方式。
核心原理:桌面合成
在 DWM 出现之前(Windows XP 及更早),应用程序窗口直接绘制到屏幕显示缓冲区。DWM 引入的桌面合成技术将这一过程改为:
- 离屏渲染:每个应用程序窗口不再直接绘制到屏幕,而是将其绘图内容重定向到视频内存中的离屏表面(Off-screen Surface)
- 统一合成:DWM 收集所有窗口的离屏表面,利用 GPU 硬件加速将它们合成为最终的桌面图像
- 输出显示:合成后的完整桌面图像输出到显示器
这种架构使得 DWM 能够实现传统渲染方式无法做到的高级视觉效果,同时避免了窗口重叠时的重绘问题和画面撕裂。
技术架构
DWM 基于 DirectX(特别是 Direct3D) 和 Windows 显示驱动程序模型(WDDM) 构建,本质上是一个全屏的 Direct3D 应用程序。它利用 GPU 进行硬件加速合成,将每个窗口创建为 Direct3D 纹理,映射到 3D 空间中的网格上进行合成。窗口的打开、关闭、最小化等过渡效果,实际上是这些 3D 矩形的变换动画。
主要功能与特性
| 功能 | 说明 |
|---|---|
| Aero Glass | 玻璃窗口效果,支持半透明和模糊背景 |
| 实时任务栏缩略图 | 鼠标悬停任务栏时显示窗口实时预览 |
| Flip / Flip 3D | Alt+Tab 窗口切换时的 3D 动画效果 |
| 窗口过渡动画 | 最小化、最大化、关闭的平滑动画 |
| 高 DPI 缩放 | 支持高分辨率显示器的 DPI 感知缩放 |
| 实时缩略图 API | 允许第三方应用在其他窗口中显示实时窗口缩略图 |
| 双缓冲机制 | 使用翻转(Flipping)技术避免画面撕裂 |
DWM 缩略图 API
DWM 提供了一组专门的 API 用于在应用程序中显示其他窗口的实时缩略图:
| API 函数 | 功能 |
|---|---|
DwmRegisterThumbnail |
在目标窗口和源窗口之间创建缩略图关系 |
DwmUpdateThumbnailProperties |
更新缩略图属性(位置、大小、透明度、裁剪区域等) |
DwmQueryThumbnailSourceSize |
获取源窗口的原始尺寸 |
DwmUnregisterThumbnail |
注销缩略图关系 |
这些 API 的关键特点是:缩略图不是静态截图,而是动态、持续的连接。源窗口的更新会实时反映在缩略图中,这正是 OnTopReplica 实现窗口克隆的核心技术基础。
系统要求与演进
- Windows Vista:DWM 首次引入,作为可选功能(Aero 主题),可手动关闭
- Windows 7:GDI 渲染获得硬件加速(配合 WDDM 1.1 驱动)
- Windows 8 及以后:DWM 成为始终开启的核心系统组件,无法禁用
- Windows 10/11:DWM 深度集成虚拟桌面、动态刷新率(DRR)、多平面叠加(MPO)等现代显示技术
为什么 OnTopReplica 依赖 DWM
OnTopReplica 的核心功能——窗口实时克隆——完全建立在 DWM 缩略图 API 之上。没有 DWM(如 Windows XP 或 DWM 被禁用的情况),程序无法获取其他窗口的实时渲染内容,因此 DWM 是 OnTopReplica 运行的必要条件。这种设计的好处是:
- 性能优异:缩略图由 DWM 在 GPU 层面合成,几乎零 CPU 开销
- 实时更新:源窗口的任何变化即时反映,无需轮询或截屏
- 系统级支持:利用原生 API,稳定可靠,兼容性好
工程
核心原理
OnTopReplica 的核心技术架构围绕 Windows DWM (Desktop Window Manager) 展开,利用系统级 API 实现高级窗口管理功能。以下是其五大核心原理:
1. 窗口克隆 — DWM 实时缩略图
OnTopReplica 并非截屏或录制视频,而是调用 Windows DWM API 在自身窗口中注册目标窗口的实时缩略图。这是 Vista+ 系统提供的原生功能,目标窗口的更新会实时反映在克隆窗口中,性能开销极低。
使用的核心 API:
| API 类别 | 具体 API | 用途 |
|---|---|---|
| DWM | DwmRegisterThumbnail |
注册窗口缩略图 |
| DWM | DwmUpdateThumbnailProperties |
更新缩略图属性(位置、大小、透明度、裁剪区域) |
| DWM | DwmUnregisterThumbnail |
注销缩略图 |
依赖的第三方库 WindowsFormsAero 提供了这些 API 的 .NET 封装(DwmManager 和 Thumbnail 类)。
2. 始终置顶 — TopMost + DWM 排除
- 基础:WinForms 的
TopMost = true属性 - 防失焦修复:在
OnDeactivate事件中重新设置TopMost - DWM 排除(Windows 7+):通过
SetWindowFlip3dPolicy、SetExcludeFromPeek、SetDisallowPeek确保窗口在 Alt+Tab 和 Aero Peek 时不会被覆盖
3. 点击转发 — 坐标映射 + PostMessage 注入
- 在克隆窗口上捕获鼠标点击事件
- 将点击坐标按比例映射到源窗口的像素坐标(补偿内边距和裁剪区域偏移)
- 通过
RealChildWindowFromPoint递归查找实际接收点击的子控件 - 使用
PostMessage向目标控件注入WM_LBUTTONDOWN/WM_LBUTTONUP等鼠标消息
4. 窗口监听 — Shell Hook
通过 RegisterShellHookWindow 注册 Shell Hook,监听系统窗口事件(创建、销毁、激活),实现:
- 当克隆窗口被关闭时自动取消克隆(WindowKeeper)
- 全局快捷键响应(HotKeyManager)
- 组切换模式自动切换窗口(GroupSwitchManager)
5. 平台适配
根据 Environment.OSVersion 检测 Windows 版本,不同版本提供不同的行为。DWM 是必需条件——没有 DWM(Windows XP 或 DWM 被禁用),程序无法运行。
核心代码
注册 DWM 缩略图(ThumbnailPanel.cs)
public void SetThumbnailHandle(WindowHandle handle, ThumbnailRegion region) {
// 关闭旧的缩略图
if (_thumbnail != null && !_thumbnail.IsInvalid) {
_thumbnail.Close();
_thumbnail = null;
}
Form owner = this.TopLevelControl as Form;
// 注册新的 DWM 缩略图!
_thumbnail = DwmManager.Register(owner, handle.Handle);
_currentRegion = region;
_regionEnabled = (region != null);
UpdateThubmnail();
}
更新缩略图属性(ThumbnailPanel.cs)
private void UpdateThubmnail() {
if (_thumbnail != null && !_thumbnail.IsInvalid){
try {
Size sourceSize = ThumbnailPixelSize;
_thumbnailSize = sourceSize.Fit(Size); // 保持宽高比适配
_padWidth = (Size.Width - _thumbnailSize.Width) / 2;
_padHeight = (Size.Height - _thumbnailSize.Height) / 2;
var target = new Rectangle(_padWidth, _padHeight, _thumbnailSize.Width, _thumbnailSize.Height);
Rectangle source = (_regionEnabled)
? _currentRegion.ComputeRegionRectangle(_thumbnail.GetSourceSize())
: new Rectangle(Point.Empty, _thumbnail.GetSourceSize());
// 更新 DWM 缩略图属性
_thumbnail.Update(target, source, ThumbnailOpacity, true, true);
}
catch {
UnsetThumbnail();
return;
}
}
}
坐标转换(ThumbnailPanel.cs)
protected Point ClientToThumbnail(Point position) {
position.X -= _padWidth;
position.Y -= _padHeight;
PointF proportionalPosition = new PointF(
(float)position.X / _thumbnailSize.Width,
(float)position.Y / _thumbnailSize.Height
);
Size source = ThumbnailPixelSize;
Point offset = (_regionEnabled) ? SelectedRegion.Offset : Point.Empty;
return new Point(
(int)((proportionalPosition.X * source.Width) + offset.X),
(int)((proportionalPosition.Y * source.Height) + offset.Y)
);
}
注入鼠标点击(Win32Helper.cs)
public static void InjectFakeMouseClick(IntPtr window, CloneClickEventArgs clickArgs) {
NPoint clientClickLocation = NPoint.FromPoint(clickArgs.ClientClickLocation);
NPoint scrClickLocation = WindowManagerMethods.ClientToScreen(window, clientClickLocation);
IntPtr hMenu = WindowMethods.GetMenu(window);
if (hMenu != IntPtr.Zero)
scrClickLocation.Y -= SystemInformation.MenuHeight;
IntPtr hChild = GetRealChildControlFromPoint(window, scrClickLocation);
NPoint clntClickLocation = WindowManagerMethods.ScreenToClient(hChild, scrClickLocation);
if (clickArgs.Buttons == MouseButtons.Left) {
if(clickArgs.IsDoubleClick)
InjectDoubleLeftMouseClick(hChild, clntClickLocation);
else
InjectLeftMouseClick(hChild, clntClickLocation);
}
}
private static void InjectLeftMouseClick(IntPtr child, NPoint clientLocation) {
IntPtr lParamClickLocation = MessagingMethods.MakeLParam(clientLocation.X, clientLocation.Y);
MessagingMethods.PostMessage(child, WM.LBUTTONDOWN, new IntPtr(MK.LBUTTON), lParamClickLocation);
MessagingMethods.PostMessage(child, WM.LBUTTONUP, new IntPtr(MK.LBUTTON), lParamClickLocation);
}
消息泵处理器(MessagePumpManager.cs)
public void Initialize(MainForm form) {
Form = form;
HookMethods.RegisterShellHookWindow(form.Handle);
Register(new WindowKeeper(), form); // 监听窗口销毁
Register(new HotKeyManager(), form); // 全局快捷键
Register(new GroupSwitchManager(), form); // 组切换模式
Register(new FlashCloner(), form); // 自动克隆闪烁窗口
}
public bool PumpMessage(ref Message msg) {
foreach (var processor in _processors.Values) {
if (processor.Process(ref msg))
return true;
}
return false;
}

在windows10上使用OnTopReplica将窗口画面复制到多个显示器上.
浙公网安备 33010602011771号