C# GDI用C将位图写入文件#
我正在尝试从给定句柄/DC的窗口捕获图像。我想拍摄具有所有透明度和像素的图像,所以我决定使用GDI/GDI+ 下面的代码是我所拥有的,但是当它“写入”位图时,它将其写入错误:换句话说,文件是不可读的。它是空白的。但它的大小是20kb:S 你知道怎么了吗 进口C# GDI用C将位图写入文件#,c#,bitmap,gdi+,gdi,C#,Bitmap,Gdi+,Gdi,我正在尝试从给定句柄/DC的窗口捕获图像。我想拍摄具有所有透明度和像素的图像,所以我决定使用GDI/GDI+ 下面的代码是我所拥有的,但是当它“写入”位图时,它将其写入错误:换句话说,文件是不可读的。它是空白的。但它的大小是20kb:S 你知道怎么了吗 进口 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Te
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Imaging
{
static class Imports
{
public enum ObjectType : uint
{
OBJ_PEN = 1,
OBJ_BRUSH = 2,
OBJ_DC = 3,
OBJ_METADC = 4,
OBJ_PAL = 5,
OBJ_FONT = 6,
OBJ_BITMAP = 7,
OBJ_REGION = 8,
OBJ_METAFILE = 9,
OBJ_MEMDC = 10,
OBJ_EXTPEN = 11,
OBJ_ENHMETADC = 12,
OBJ_ENHMETAFILE = 13
}
public enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062,
CAPTUREBLT = 0x40000000
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left, Top, Right, Bottom;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BITMAPFILEHEADER
{
public ushort bfType;
public uint bfSize;
public ushort bfReserved1;
public ushort bfReserved2;
public uint bfOffBits;
}
[StructLayout(LayoutKind.Sequential)]
public 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;
public void Init()
{
biSize = (uint)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
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;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public uint[] cols;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAP
{
public int bmType;
public int bmWidth;
public int bmHeight;
public int bmWidthBytes;
public ushort bmPlanes;
public ushort bmBitsPixel;
public IntPtr bmBits;
};
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr WindowHandle);
[DllImport("user32.dll")]
public static extern void ReleaseDC(IntPtr WindowHandle, IntPtr DC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr WindowHandle, ref Rect rect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr DC, uint nFlags);
[DllImport("user32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);
[DllImport("gdi32.dll")]
public static extern IntPtr GetCurrentObject(IntPtr DC, ObjectType uObjectType);
[DllImport("gdi32.dll")]
public static extern int GetObject(IntPtr hObject, int nCount, ref BITMAP lpObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr DC);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool DeleteDC(IntPtr DC);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr DC, int nWidth, int nHeight);
[DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
public static extern IntPtr SelectObject(IntPtr DC, IntPtr hgdiobj);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll")]
static extern bool FillRgn(IntPtr DC, IntPtr hrgn, IntPtr hbr);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateSolidBrush(uint crColor);
[DllImport("gdi32.dll")]
public static extern int GetDIBits(IntPtr DC, IntPtr hbmp, uint uStartScan, uint cScanLines, [Out] byte[] lpvBits, ref BITMAPINFO lpbmi, uint uUsage);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(IntPtr DC, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr SrcDC, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
}
}
我的PBitmap类:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace Imaging
{
class PBitmap
{
private Bitmap Img = null;
private Imports.BITMAPINFO Info;
private Imports.BITMAPFILEHEADER bFileHeader;
public PBitmap(IntPtr Window, int X, int Y, uint BitsPerPixel = 24)
{
IntPtr DC = Imports.GetDC(Window);
Imports.BITMAP Bmp = new Imports.BITMAP();
IntPtr hBmp = Imports.GetCurrentObject(DC, Imports.ObjectType.OBJ_BITMAP);
if (Imports.GetObject(hBmp, Marshal.SizeOf(Bmp), ref Bmp) == 0)
throw new Exception("ERROR_INVALID_WINDOW_HANDLE.");
Imports.Rect BMBox = new Imports.Rect();
Imports.GetWindowRect(Window, ref BMBox); //GetClientRect
int width = BMBox.Right - BMBox.Left;
int height = BMBox.Bottom - BMBox.Top;
IntPtr MemDC = Imports.GetDC(IntPtr.Zero);
IntPtr SDC = Imports.CreateCompatibleDC(MemDC);
IntPtr hSBmp = Imports.CreateCompatibleBitmap(MemDC, width, height);
Imports.DeleteObject(Imports.SelectObject(SDC, hSBmp));
Imports.BitBlt(SDC, 0, 0, width, height, DC, X, Y, Imports.TernaryRasterOperations.SRCCOPY);
long size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Info = new Imports.BITMAPINFO();
bFileHeader = new Imports.BITMAPFILEHEADER();
Info.biSize = (uint)Marshal.SizeOf(Info);
Info.biWidth = width;
Info.biHeight = height;
Info.biPlanes = 1;
Info.biBitCount = (ushort)BitsPerPixel;
Info.biCompression = 0;
Info.biSizeImage = (uint)size;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = (uint)(Marshal.SizeOf(bFileHeader) + Marshal.SizeOf(Info));
bFileHeader.bfSize = (uint)(bFileHeader.bfOffBits + size);
byte[] ImageData = new byte[size];
Imports.GetDIBits(SDC, hSBmp, 0, (uint)height, ImageData, ref Info, 0);
var bw = new BinaryWriter(File.Open("C:/Users/*******/Desktop/Foo.bmp", FileMode.OpenOrCreate));
bw.Write((short)0x4D42);
bw.Write((uint)(bFileHeader.bfOffBits + size));
bw.Write((uint)0);
bw.Write((uint)54);
bw.Write((uint)Info.biSize);
bw.Write(width);
bw.Write(height);
bw.Write((short)Info.biPlanes);
bw.Write(BitsPerPixel);
bw.Write((uint)0);
bw.Write((uint)size);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write((uint)0);
bw.Write(ImageData);
bw.Flush();
bw.Close();
bw.Dispose();
Imports.DeleteDC(SDC);
Imports.DeleteObject(hSBmp);
Imports.ReleaseDC(IntPtr.Zero, MemDC);
Imports.ReleaseDC(Window, DC);
}
}
}
如果你正在寻找一个快速的创可贴解决方案(它只需要工作,不经常使用,等等),只需将图像加载回.NET,并通过启用PNG压缩重新保存它。NET framework将为您处理此问题,并且文件大小将更加优化。当然,如果这是一个实时应用程序,这并不理想
> P>如果你正在寻找一个快速的创可贴解决方案(它只需要工作,不经常使用,等等),只需将图像加载回.NET,并通过启用PNG压缩将其重新保存即可。NET framework将为您处理此问题,并且文件大小将更加优化。当然,如果这是一个实时应用程序,这并不理想 内存中的数据正确吗?我编辑了这篇文章。我将它从Marshal.StructToPtr改为bw.Write,在这里我写入结构的所有参数。这似乎工作,除了它现在的文件是3.92兆字节的大小!它看起来像:它的大小是1366x744,我不太清楚为什么它是3.92mb!在C++中,相同的代码工作得很好,图像最多是100-200 KB。显然,如果你得到一个200 KB文件,C++中就不会使用相同的方法。您正在存储GetDIBits
的(未压缩)结果。仅位图数据的大小(不计算头数)的快速估计为1366*744*4=4065216字节=3.88 MB,但我是:完全相同的计算、相同的函数和顺序。。这就是我试图移植的代码。它可以在代码块中正常工作,并且可以很好地保存图像,而且图像很小。根据C++代码中的这行,代码>大小=((宽度×BitsPerPixel + 31)/ 32)* 4 *高度;<代码>,我想说的是200kB文件中的图像尺寸差异很大。内存中的数据正确吗?我编辑了这篇文章。我将它从Marshal.StructToPtr改为bw.Write,在这里我写入结构的所有参数。这似乎工作,除了它现在的文件是3.92兆字节的大小!它看起来像:它的大小是1366x744,我不太清楚为什么它是3.92mb!在C++中,相同的代码工作得很好,图像最多是100-200 KB。显然,如果你得到一个200 KB文件,C++中就不会使用相同的方法。您正在存储GetDIBits
的(未压缩)结果。仅位图数据的大小(不计算头数)的快速估计为1366*744*4=4065216字节=3.88 MB,但我是:完全相同的计算、相同的函数和顺序。。这就是我试图移植的代码。它可以在代码块中正常工作,并且可以很好地保存图像,而且图像很小。根据C++代码中的这行,代码>大小=((宽度×BitsPerPixel + 31)/ 32)* 4 *高度;<代码>,我想是在200 KB文件中的图像不同。这是个好主意,但是我想我会把C++代码放到DLL中,然后加载它。我真的很想一个人在C#完成这件事。我只想把它完成,因为我想做屏幕截图,以保持C#的透明度。但我想现在不行。。无论如何,谢谢。这是个好主意,但是我想我会把C++代码放到DLL中,然后加载它。我真的很想一个人在C#完成这件事。我只想把它完成,因为我想做屏幕截图,以保持C#的透明度。但我想现在不行。。无论如何,谢谢你。