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

自制指定窗口录屏工具无法移动目标窗口的问题求助

问题描述

我开发了一款可录制用户选定指定窗口的录屏工具,但录制过程中程序会强制目标窗口始终处于激活状态,导致完全无法移动窗口或调整窗口大小,甚至无法停止录制。请问有什么解决办法?

原代码片段

下拉框选择事件代码

private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
    if (Handlebox.SelectedIndex == -1) return;
        handle_name = Handlebox.SelectedItem.ToString();
}

向下拉框添加进程的代码

foreach (Process process in processlist)
{
    if (!String.IsNullOrEmpty(process.MainWindowTitle))
    {
        Handlebox.Items.Add(process.ProcessName);
    }
}

录屏核心代码

// Record video:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);

private const int SW_RESTORE = 9;

[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

public Bitmap CaptureApplication(string procName)
{
    Process proc;

    // Cater for cases when the process can't be located.
    try
    {
        proc = Process.GetProcessesByName(procName)[0];
    }
    catch (IndexOutOfRangeException e)
    {
        return null;
    }

    // You need to focus on the application
    SetForegroundWindow(proc.MainWindowHandle);
    ShowWindow(proc.MainWindowHandle, SW_RESTORE);

    // Delay
    Thread.Sleep(1000);

    Rect rect = new Rect();
    IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);

    // sometimes it gives error.
    while (error == (IntPtr)0)
    {
        error = GetWindowRect(proc.MainWindowHandle, ref rect);
    }

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    Graphics.FromImage(bmp).CopyFromScreen(rect.left,rect.top,0,0,new Size(width, height),CopyPixelOperation.SourceCopy);

    return bmp;
}

public void RecordVideo()
{
    // Keep track of time:
    watch.Start();

    // Save screenshot:
    string name = tempPath + "//screenshot-" + fileCount + ".png";
    CaptureApplication(handle_name).Save(name, ImageFormat.Png);
    inputImageSequence.Add(name);
    fileCount++;

    // Dispose of bitmap:
    CaptureApplication(handle_name).Dispose();
}

解决办法及修改后的代码

问题根源在于每次截图都强制激活并恢复目标窗口,且录制时重复调用截图方法,导致窗口被反复抢占焦点。以下是针对性修改:

1. 改用窗口句柄定位目标窗口(避免同名进程冲突)

原代码用进程名查找窗口易出现多进程冲突,改为直接存储窗口句柄和标题:

修改下拉框填充代码:

// 先清空下拉框
Handlebox.Items.Clear();
foreach (Process process in Process.GetProcesses())
{
    if (!string.IsNullOrEmpty(process.MainWindowTitle))
    {
        // 存储窗口句柄和标题,用Tuple示例
        Handlebox.Items.Add(new Tuple<IntPtr, string>(process.MainWindowHandle, process.MainWindowTitle));
        // 设置下拉框显示标题
        Handlebox.DisplayMember = "Item2";
    }
}

修改下拉框选择事件:

private IntPtr targetWindowHandle; // 全局变量存储目标窗口句柄

private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
    if (Handlebox.SelectedIndex == -1)
    {
        targetWindowHandle = IntPtr.Zero;
        return;
    }
    var selectedItem = (Tuple<IntPtr, string>)Handlebox.SelectedItem;
    targetWindowHandle = selectedItem.Item1;
}

2. 移除强制激活窗口的代码,优化截图逻辑

不需要激活窗口也能获取其位置并截图,删除SetForegroundWindow和ShowWindow(SW_RESTORE),同时避免死循环重试:

修改截图核心方法:

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

public Bitmap CaptureWindow(IntPtr hWnd)
{
    if (hWnd == IntPtr.Zero) return null;

    Rect rect = new Rect();
    // 最多重试3次获取窗口位置,避免死循环
    int retryCount = 0;
    IntPtr error = GetWindowRect(hWnd, ref rect);
    while (error == IntPtr.Zero && retryCount < 3)
    {
        Thread.Sleep(100);
        error = GetWindowRect(hWnd, ref rect);
        retryCount++;
    }
    if (error == IntPtr.Zero) return null;

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;
    // 处理窗口最小化的情况
    if (width <= 0 || height <= 0) return null;

    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
    }
    return bmp;
}

3. 优化录制逻辑,避免重复截图

原代码在RecordVideo中调用两次截图方法,改为调用一次并正确释放资源:

public void RecordVideo()
{
    watch.Start();

    string name = Path.Combine(tempPath, $"screenshot-{fileCount}.png");
    using (Bitmap bmp = CaptureWindow(targetWindowHandle))
    {
        if (bmp != null)
        {
            bmp.Save(name, ImageFormat.Png);
            inputImageSequence.Add(name);
            fileCount++;
        }
    }
}

额外优化建议

  • 录制过程用后台线程执行截图操作,避免阻塞UI线程,确保停止录制按钮可正常响应。
  • 录制时实时检测窗口状态(如关闭、最小化),及时抛出提示或暂停录制。

内容的提问来源于stack exchange,提问作者SacredTravis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:45:39