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

WPF应用在第二显示器截图异常:黑屏与尺寸错误问题求助

问题
  • WPF透明窗口截图需求:需同时截取窗口及后方背景
  • 主显示器上截图正常,但窗口完全移至第二显示器时,截图尺寸错误且黑屏;窗口跨屏时仍可正常工作
  • 当前使用的截图函数如下:
public void SaveSnapshot2(int count)
{
    string imageName = "image" + count.ToString() + ".jpeg";
    string basePath = Path.GetFullPath(Environment.CurrentDirectory);
    string folderPath = Path.Combine(basePath, "Snapshots");
    string fullPath = Path.Combine(folderPath, imageName);

    if (!Directory.Exists(folderPath))
    {
        Directory.CreateDirectory(folderPath);
    }

    System.Windows.Point relativeWindowPosition = App.Current.MainWindow.PointToScreen(new System.Windows.Point(0, 0));
    
    System.Windows.Point relativeBottomRightWindowPosition = App.Current.MainWindow.PointToScreen(new System.Windows.Point(App.Current.MainWindow.Width, App.Current.MainWindow.Height));
    
    System.Windows.Point actualWidthHeight = new System.Windows.Point((relativeBottomRightWindowPosition.X - relativeWindowPosition.X), (relativeBottomRightWindowPosition.Y - relativeWindowPosition.Y));

    System.Drawing.Size convertedSize = new System.Drawing.Size((int)actualWidthHeight.X, (int)actualWidthHeight.Y);
    
    Bitmap Screenshot = new Bitmap((int)actualWidthHeight.X, (int)actualWidthHeight.Y, PixelFormat.Format32bppArgb);
    using (Graphics g = Graphics.FromImage(Screenshot))
    {
        // Crops the screenshot
        // The relativePoint is where the top left corner of the image is. This is correct. 
        g.CopyFromScreen((int)relativeWindowPosition.X, (int)relativeWindowPosition.Y, 0, 0, convertedSize); // or (0, 0, 0, 0, Screenshot.Size) to get the whole bitmap image
    }

    try
    {
        Screenshot.Save(fullPath, ImageFormat.Jpeg);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message + ", the image did not save.");
    }
    Screenshot.Dispose();
}
回答

多显示器坐标系说明

Windows多显示器采用虚拟桌面坐标系:主显示器左上角是原点(0,0),副显示器的坐标可能为负数(比如主显示器在左,副显示器在其左侧时),具体取决于显示器的排列方式。你的问题核心是CopyFromScreen不支持读取负坐标区域的画面,导致副屏截图黑屏。

当前实现的问题

  1. 通过PointToScreen计算右下角再反推宽高的方式冗余,且未考虑DPI缩放:WPF的Width/Height是逻辑像素,转成屏幕物理像素会有偏差
  2. CopyFromScreen不支持负坐标,副显示器坐标为负时无法正常读取画面
  3. 资源释放逻辑不严谨,未用finally确保Bitmap被释放

修复后的代码

改用Win32的BitBlt API实现,支持跨任意显示器的截图,同时处理DPI缩放问题:

public void SaveSnapshot(int count)
{
    string imageName = $"image{count}.jpeg";
    string folderPath = Path.Combine(Environment.CurrentDirectory, "Snapshots");
    string fullPath = Path.Combine(folderPath, imageName);

    Directory.CreateDirectory(folderPath); // 无需提前判断目录是否存在,该方法会自动处理

    var mainWindow = App.Current.MainWindow;
    // 获取窗口在屏幕上的真实物理位置和尺寸(适配DPI)
    var windowTopLeft = mainWindow.PointToScreen(new System.Windows.Point(0, 0));
    var windowBottomRight = mainWindow.PointToScreen(new System.Windows.Point(mainWindow.ActualWidth, mainWindow.ActualHeight));
    
    int actualWidth = (int)Math.Abs(windowBottomRight.X - windowTopLeft.X);
    int actualHeight = (int)Math.Abs(windowBottomRight.Y - windowTopLeft.Y);

    Bitmap screenshot = new Bitmap(actualWidth, actualHeight, PixelFormat.Format32bppArgb);
    using (Graphics g = Graphics.FromImage(screenshot))
    {
        IntPtr screenDC = GetDC(IntPtr.Zero);
        IntPtr bitmapDC = g.GetHdc();
        // 使用BitBlt复制屏幕区域,支持负坐标
        BitBlt(bitmapDC, 0, 0, actualWidth, actualHeight, screenDC, (int)windowTopLeft.X, (int)windowTopLeft.Y, 0xCC0020);
        g.ReleaseHdc(bitmapDC);
        ReleaseDC(IntPtr.Zero, screenDC);
    }

    try
    {
        screenshot.Save(fullPath, ImageFormat.Jpeg);
    }
    catch (Exception ex)
    {
        MessageBox.Show($"{ex.Message},截图保存失败");
    }
    finally
    {
        screenshot.Dispose();
    }
}

// 需添加的Win32 API声明
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);

[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);

关键优化点

  • 用BitBlt替代CopyFromScreen,支持读取虚拟桌面任意坐标(包括负数)的画面
  • 使用ActualWidth/ActualHeight获取窗口真实渲染尺寸,适配DPI缩放
  • 简化目录创建逻辑,Directory.CreateDirectory无需提前判断
  • 用finally块确保Bitmap资源被释放,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 08:36:10