调用FromHbitmap遇GDI+通用异常的500万像素显示器适配求助
解决GDI+ FromHBitmap在高分辨率显示器下的异常问题
已知问题根源是GDI+对图像高度的硬限制(最大65500像素),在高分辨率显示器下捕获的窗口位图高度超过该阈值时触发异常。以下是几个可行的适配方案:
方案1:改用WPF的BitmapSource实现跨框架互操作
WPF的成像系统没有GDI+的像素尺寸限制,WinForms项目可通过引用WPF相关程序集实现句柄转换:
- 右键项目→添加引用,勾选
PresentationCore和WindowsBase - 使用
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
相关产品推荐
相关产品推荐

