You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 02:27:22