C#开发屏幕录制工具如何实现仅捕获指定窗口内容
C#屏幕录制工具指定窗口录制改造方案
改造核心逻辑为通过Windows系统API枚举顶层窗口供用户选择,录制时实时获取目标窗口的屏幕坐标完成区域截图,全程不影响原有全屏录制逻辑。
步骤1:补充所需Win32 API定义
在Recorder Script.cs的NativeMethods类中追加以下API和结构体定义,用于窗口枚举、位置获取:
// 委托定义 public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); // 枚举所有顶层窗口 [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); // 获取窗口标题 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); // 判断窗口是否可见 [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindowVisible(IntPtr hWnd); // 获取窗口矩形区域(屏幕坐标) [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); // 矩形结构体 [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
注意:需要在代码文件顶部引入System.Text、System.Runtime.InteropServices命名空间
步骤2:修改ScreenRecorder类支持窗口绑定
在ScreenRecorder类中追加窗口绑定相关逻辑,支持实时更新捕获区域:
- 新增私有字段存储目标窗口句柄
- 新增窗口绑定方法、捕获区域更新方法
- 修改截图逻辑,绑定窗口时每次截图前同步最新窗口位置
- 修复原有代码中视频去重循环条件的bug
// 在ScreenRecorder类私有变量区新增 private IntPtr _targetWindowHandle = IntPtr.Zero; /// <summary> /// 绑定要录制的目标窗口 /// </summary> public void BindTargetWindow(IntPtr hWnd) { _targetWindowHandle = hWnd; UpdateCaptureBounds(); } /// <summary> /// 实时更新目标窗口的捕获区域(处理窗口移动、尺寸变化场景) /// </summary> private bool UpdateCaptureBounds() { // 全屏模式直接返回 if (_targetWindowHandle == IntPtr.Zero) return true; if (!GetWindowRect(_targetWindowHandle, out RECT rect)) return false; int width = rect.Right - rect.Left; int height = rect.Bottom - rect.Top; // 窗口最小化/尺寸异常时返回无效 if (width <= 0 || height <= 0) return false; bounds = new Rectangle(rect.Left, rect.Top, width, height); return true; }
修改RecordVideo方法,截图前先更新捕获区域:
public void RecordVideo() { watch.Start(); // 绑定窗口模式下先同步最新窗口位置 if (_targetWindowHandle != IntPtr.Zero && !UpdateCaptureBounds()) { // 窗口无效(最小化/关闭)时跳过当前帧 return; } using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height)) { using (Graphics g = Graphics.FromImage(bitmap)) { g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size); } string name = tempPath + "//screenshot-" + fileCount + ".png"; bitmap.Save(name, ImageFormat.Png); inputImageSequence.Add(name); fileCount++; bitmap.Dispose(); } }
修复SaveVideo方法中的循环条件bug(原条件导致去重逻辑完全不执行):
// 原代码 for(int k=1; k > fileCount; k++) 改为 for(int k=1; k < fileCount - 1; k++) { // 原有图像去重逻辑保持不变 }
步骤3:实现窗口选择交互逻辑
在Form1.cs中补全SelectWindow_Click事件逻辑,点击按钮时枚举所有可见窗口供用户选择:
// 窗体级变量:存储枚举到的窗口列表 private struct WindowItem { public IntPtr Handle; public string Title; public override string ToString() => Title; } private List<WindowItem> _windowList = new List<WindowItem>(); // 枚举窗口回调方法 private bool EnumWindowCallback(IntPtr hWnd, IntPtr lParam) { if (ScreenRecorder.NativeMethods.IsWindowVisible(hWnd)) { StringBuilder titleBuilder = new StringBuilder(256); ScreenRecorder.NativeMethods.GetWindowText(hWnd, titleBuilder, 256); string title = titleBuilder.ToString(); // 过滤无标题的系统隐藏窗口 if (!string.IsNullOrWhiteSpace(title)) { _windowList.Add(new WindowItem { Handle = hWnd, Title = title }); } } return true; } // 补全SelectWindow按钮点击事件 private void SelectWindow_Click(object sender, EventArgs e) { _windowList.Clear(); ScreenRecorder.NativeMethods.EnumWindows(EnumWindowCallback, IntPtr.Zero); // 弹出窗口选择对话框 Form selectForm = new Form() { Text = "选择录制窗口", Width = 420, Height = 160, StartPosition = FormStartPosition.CenterParent, FormBorderStyle = FormBorderStyle.FixedDialog, MaximizeBox = false, MinimizeBox = false }; ComboBox windowCombo = new ComboBox() { Dock = DockStyle.Top, DropDownStyle = ComboBoxStyle.DropDownList, Top = 20, Margin = new Padding(10) }; windowCombo.Items.AddRange(_windowList.ToArray()); if (windowCombo.Items.Count > 0) windowCombo.SelectedIndex = 0; Button confirmBtn = new Button() { Text = "确认选择", Dock = DockStyle.Bottom, Height = 30 }; selectForm.Controls.Add(windowCombo); selectForm.Controls.Add(confirmBtn); confirmBtn.Click += (s, args) => { if (!folderSelected) { MessageBox.Show("请先选择输出文件夹", "提示"); return; } if (windowCombo.SelectedItem is WindowItem selectedWin) { screenRec.BindTargetWindow(selectedWin.Handle); MessageBox.Show($"已绑定录制窗口:{selectedWin.Title}", "提示"); selectForm.DialogResult = DialogResult.OK; selectForm.Close(); } }; selectForm.ShowDialog(); }
可选优化项
- 若需要仅录制窗口客户区(排除标题栏、边框),可替换
GetWindowRect为GetClientRect+ClientToScreenAPI获取客户区坐标即可 - 可增加窗口最小化、关闭时的状态提示,避免录制空白内容
- 可替换简易选择弹窗为带窗口图标的列表,提升识别度
- 若需要录制被遮挡的窗口内容,
CopyFromScreen无法实现,需要换用PrintWindowAPI发送WM_PRINT消息给目标窗口捕获画面
内容的提问来源于stack exchange,提问作者Kostas G
相关产品推荐
相关产品推荐

