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

如何在C# .NET中捕获非活动应用窗口的特定区域?

截取非前台窗口的特定区域为Bitmap

我需要将非前台应用程序窗口的特定区域保存为Bitmap,而非整个窗口。目前我用以下代码查找窗口并截图,但不知道如何修改RECT的起点和大小,只截取目标窗口(比如记事本)的指定部分。

public static IntPtr WinGetHandle(string wName)
{
     foreach (Process pList in Process.GetProcesses())
         if (pList.MainWindowTitle.Contains(wName))
             return pList.MainWindowHandle;

     return IntPtr.Zero;
}

[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hWnd, IntPtr hdcBlt, int nFlags);

public static void PrintThisWindow(IntPtr hwnd)
{
    // Create the Rectangle
    RECT rc = new RECT();
    GetWindowRect(hwnd, out rc);

    // Capture bmp from hwnd
    Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
    Graphics gfxBmp = Graphics.FromImage(bmp);
    IntPtr hdcBitmap = gfxBmp.GetHdc();
    PrintWindow(hwnd, hdcBitmap, 0);

    // Dispose
    gfxBmp.ReleaseHdc(hdcBitmap);
    gfxBmp.Dispose();

    //bitmap.Save
    bmp.Save("Example.png");
}

//Whatever window you want to test.
IntPtr hwnd = WinGetHandle("New Text Document.txt - Notepad");
PrintThisWindow(hwnd);

我试过手动设置RECT:RECT rc = new RECT(100, 100, 200, 200),也尝试过设置位置和大小:rc.Location = new Point(100, 100) 和 rc.Size = new Size(100, 100),但都没成功。我找了很久适配的函数,只找到CopyFromScreen:captureGraphics.CopyFromScreen(200, 200, 0, 0, new Size(100, 100)),它能按指定起点和大小截取,但这是从整个屏幕复制,不是针对特定窗口的。

以下是RECT类定义:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    private int _Left;
    private int _Top;
    private int _Right;
    private int _Bottom;

    public RECT(RECT Rectangle) : this(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom)
    {
    }
    public RECT(int Left, int Top, int Right, int Bottom)
    {
         _Left = Left;
         _Top = Top;
         _Right = Right;
         _Bottom = Bottom;
    }

    public int X
    {
         get { return _Left; }
         set { _Left = value; }
    }
    public int Y
    {
         get { return _Top; }
         set { _Top = value; }
    }
    public int Left
    {
         get { return _Left; }
         set { _Left = value; }
    }
    public int Top
    {
         get { return _Top; }
         set { _Top = value; }
    }
    public int Right
    {
         get { return _Right; }
         set { _Right = value; }
    }
    public int Bottom
    {
         get { return _Bottom; }
         set { _Bottom = value; }
    }
    public int Height
    {
         get { return _Bottom - _Top; }
         set { _Bottom = value + _Top; }
    }
    public int Width
    {
         get { return _Right - _Left; }
         set { _Right = value + _Left; }
    }
    public Point Location
    {
         get { return new Point(Left, Top); }
         set
         {
              _Left = value.X;
              _Top = value.Y;
         }
    }
    public Size Size
    {
         get { return new Size(Width, Height); }
         set
         {
              _Right = value.Width + _Left;
              _Bottom = value.Height + _Top;
         }
    }

    public static implicit operator Rectangle(RECT Rectangle)
    {
         return new Rectangle(Rectangle.Left, Rectangle.Top, Rectangle.Width, Rectangle.Height);
    }
    public static implicit operator RECT(Rectangle Rectangle)
    {
         return new RECT(Rectangle.Left, Rectangle.Top, Rectangle.Right, Rectangle.Bottom);
    }
    public static bool operator ==(RECT Rectangle1, RECT Rectangle2)
    {
         return Rectangle1.Equals(Rectangle2);
    }
    public static bool operator !=(RECT Rectangle1, RECT Rectangle2)
    {
         return !Rectangle1.Equals(Rectangle2);
    }

    public override string ToString()
    {
         return "{Left: " + _Left + "; " + "Top: " + _Top + "; Right: " + _Right + "; Bottom: " + _Bottom + "}";
    }

    public override int GetHashCode()
    {
         return ToString().GetHashCode();
    }

    public bool Equals(RECT Rectangle)
    {
         return Rectangle.Left == _Left && Rectangle.Top == _Top && Rectangle.Right == _Right && Rectangle.Bottom == _Bottom;
    }

    public override bool Equals(object Object)
    {
         if (Object is RECT)
         {
              return Equals((RECT)Object);
         }
         else if (Object is Rectangle)
         {
              return Equals(new RECT((Rectangle)Object));
         }

         return false;
    }
}

解决方案

核心问题是PrintWindow默认会绘制整个窗口,直接修改RECT无法限制截取区域。以下两种方案可实现需求:

方案1:裁剪完整窗口截图(简单易实现)

先捕获整个窗口的Bitmap,再从中裁剪出目标区域:

public static void PrintWindowRegion(IntPtr hwnd, int offsetX, int offsetY, int width, int height)
{
    // 获取窗口整体矩形
    RECT windowRect = new RECT();
    GetWindowRect(hwnd, out windowRect);

    // 捕获整个窗口
    Bitmap fullBmp = new Bitmap(windowRect.Width, windowRect.Height, PixelFormat.Format32bppArgb);
    using (Graphics gfx = Graphics.FromImage(fullBmp))
    {
        IntPtr hdc = gfx.GetHdc();
        PrintWindow(hwnd, hdc, 0);
        gfx.ReleaseHdc(hdc);
    }

    // 裁剪指定区域(offsetX、offsetY为窗口内部相对坐标)
    Bitmap regionBmp = fullBmp.Clone(new Rectangle(offsetX, offsetY, width, height), fullBmp.PixelFormat);
    
    // 保存裁剪后的Bitmap
    regionBmp.Save("WindowRegion.png");
    
    // 释放资源
    fullBmp.Dispose();
    regionBmp.Dispose();
}

// 使用示例:截取记事本窗口内从(100,100)开始,宽高100x100的区域
IntPtr hwnd = WinGetHandle("New Text Document.txt - Notepad");
PrintWindowRegion(hwnd, 100, 100, 100, 100);

方案2:直接绘制指定区域(更高效)

通过BitBlt结合窗口DC直接捕获目标区域,需额外引入API:

[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

public static void CaptureWindowRegionDirect(IntPtr hwnd, int clientX, int clientY, int width, int height)
{
    // 创建目标Bitmap
    Bitmap regionBmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    using (Graphics gfx = Graphics.FromImage(regionBmp))
    {
        IntPtr hdcDest = gfx.GetHdc();
        IntPtr hdcSrc = GetWindowDC(hwnd);
        
        // 从窗口的(clientX, clientY)位置复制指定大小到目标Bitmap
        BitBlt(hdcDest, 0, 0, width, height, hdcSrc, clientX, clientY, 0x00CC0020); // SRCCOPY参数
        
        ReleaseDC(hwnd, hdcSrc);
        gfx.ReleaseHdc(hdcDest);
    }
    
    regionBmp.Save("DirectRegion.png");
    regionBmp.Dispose();
}

注意事项

  • 方案中的offsetX/clientX、offsetY/clientY是相对于窗口客户区的坐标(不含标题栏、边框),若需基于窗口整体坐标,需通过GetClientRect和ClientToScreen进行坐标转换。
  • 针对高DPI缩放的窗口,需调整坐标计算逻辑,确保截取区域准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 14:14:35