2013-04-15 47 views
1

我试图从Handle/DC窗口捕获图像。我想用所有的透明度和像素来捕捉图像,所以我决定使用GDI/GDI +。GDI将位图写入C文件中的文件#

下面的代码就是我的,但是当它“写入”位图时,它会写错:S换句话说,该文件是不可读的。它是空白的。然而它的大小为20kb:S

任何想法是什么错?

Imports.cs

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); 
     } 
    } 
} 
+1

数据在内存中是否正确? –

+0

我编辑了这篇文章。我将它从Marshal.StructToPtr更改为bw.Write,我在其中写入结构的所有参数。这似乎工作,只是它现在的文件是3.92兆字节的大小! 它看起来像:http://i.imgur.com/vrSqo9x.png 这是1366x744大小我不太确定为什么它是3.92mb!在C++中,相同的代码工作正常,图像最多为100-200kb。 – Brandon

+0

显然,如果你得到一个200kB的文件,你不会在C++中使用相同的方法。您正在存储'GetDIBits'的(未压缩)结果。仅对位图数据的大小(不计算标题)的快速估计是1366 * 744 * 4 = 4065216字节= 3.88 MB –

回答

1

如果你正在寻找一个快速创可贴修复来完成这件事(它只是需要工作,而不是使用经常等)考虑将图像加载回.NET,并在启用PNG压缩的情况下重新保存。 .NET框架将为您处理这个问题,文件大小将会更加优化。当然,如果这是实时应用程序,这并不理想。

+0

这是一个不错的主意,但我认为我只是将C++代码放入DLL并加载它。我真的希望只用C#就能完成它。我只想完成它,因为我想在C#中执行保持透明度的截图。但我现在不想现在..谢谢。 – Brandon