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

调用FromHbitmap遇GDI+通用异常的500万像素显示器适配求助

解决GDI+ FromHBitmap在高分辨率显示器下的异常问题

已知问题根源是GDI+对图像高度的硬限制(最大65500像素),在高分辨率显示器下捕获的窗口位图高度超过该阈值时触发异常。以下是几个可行的适配方案:

方案1:改用WPF的BitmapSource实现跨框架互操作

WPF的成像系统没有GDI+的像素尺寸限制,WinForms项目可通过引用WPF相关程序集实现句柄转换:

  1. 右键项目→添加引用,勾选PresentationCore和WindowsBase
  2. 使用System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap替代FromHBitmap:
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Interop;

// 传入窗口捕获得到的hbitmap句柄
public BitmapSource ConvertHBitmapToBitmapSource(IntPtr hbitmap)
{
    BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(
        hbitmap,
        IntPtr.Zero,
        Int32Rect.Empty,
        BitmapSizeOptions.FromEmptyOptions());
    
    // 释放原始HBitmap,避免内存泄漏
    DeleteObject(hbitmap);
    return bitmapSource;
}

// 声明Win32 API用于释放GDI对象
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);

若后续无需转回GDI+ Bitmap,建议直接用BitmapSource完成复制、显示等操作;若必须转换,仅在最终尺寸不超过GDI+限制时执行。

方案2:通过Win32 API手动读取位图数据

直接调用Win32底层API读取像素数据,完全绕开GDI+的封装限制:

using System.Runtime.InteropServices;
using System.Drawing.Imaging;

[StructLayout(LayoutKind.Sequential)]
private struct BITMAP
{
    public int bmType;
    public int bmWidth;
    public int bmHeight;
    public int bmWidthBytes;
    public ushort bmPlanes;
    public ushort bmBitsPixel;
    public IntPtr bmBits;
}

[DllImport("gdi32.dll")]
private static extern int GetObject(IntPtr hObject, int nCount, out BITMAP lpObject);

[DllImport("gdi32.dll")]
private static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint cScanLines, byte[] lpvBits, ref BITMAPINFO lpbmi, uint fuColorUse);

[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFOHEADER
{
    public uint biSize;
    public int biWidth;
    public int biHeight;
    public ushort biPlanes;
    public ushort biBitCount;
    public uint biCompression;
    public uint biSizeImage;
    public int biXPelsPerMeter;
    public int biYPelsPerMeter;
    public uint biClrUsed;
    public uint biClrImportant;
}

[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
    public BITMAPINFOHEADER bmiHeader;
    public IntPtr bmiColors;
}

public Bitmap CreateBitmapFromHBitmap(IntPtr hbitmap)
{
    // 获取位图基础信息
    BITMAP bmp = new BITMAP();
    GetObject(hbitmap, Marshal.SizeOf(bmp), out bmp);
    
    // 构造BITMAPINFO结构
    BITMAPINFO info = new BITMAPINFO();
    info.bmiHeader.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
    info.bmiHeader.biWidth = bmp.bmWidth;
    info.bmiHeader.biHeight = bmp.bmHeight;
    info.bmiHeader.biPlanes = bmp.bmPlanes;
    info.bmiHeader.biBitCount = bmp.bmBitsPixel;
    info.bmiHeader.biCompression = 0; // 使用BI_RGB无压缩格式
    
    // 计算像素数据缓冲区大小
    int bufferSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
    byte[] pixelBuffer = new byte[bufferSize];
    
    // 获取设备上下文并读取像素数据
    using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
    {
        IntPtr hdc = g.GetHdc();
        GetDIBits(hdc, hbitmap, 0, (uint)bmp.bmHeight, pixelBuffer, ref info, 0);
        g.ReleaseHdc(hdc);
    }
    
    // 创建Bitmap并写入像素数据
    Bitmap bitmap = new Bitmap(bmp.bmWidth, bmp.bmHeight, PixelFormat.Format32bppArgb);
    BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bmp.bmWidth, bmp.bmHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
    Marshal.Copy(pixelBuffer, 0, bitmapData.Scan0, bufferSize);
    bitmap.UnlockBits(bitmapData);
    
    // 释放原始GDI对象句柄
    DeleteObject(hbitmap);
    return bitmap;
}

// 声明Win32 API用于释放GDI对象
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);

该方案直接操作像素数据,无尺寸限制,适合需要保留GDI+ Bitmap对象的场景。

方案3:分块处理大位图

若业务场景允许,可将高分辨率位图按高度分割为多个子块(每个子块高度≤65500),分别用FromHBitmap处理后,按需合并或分块执行复制、显示等操作。例如复制时分块写入剪贴板,显示时分块渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:36:09