Win32 API Constants
This implementation provides efficient window capture capabiilties using BitBlt operations. For a 1920×1080 window, capture operations typically compelte in 2-4 milliseconds.
Win32Constants Class
using System.ComponentModel;
public static class Win32Constants
{
public enum ColorTableUsage : uint
{
RGB_COLORS = 0x00,
PALETTE_COLORS = 0x01,
PALETTE_INDICES = 0x02
}
public enum CompressionType : uint
{
NONE = 0,
RLE_8BIT = 1,
RLE_4BIT = 2,
BIT_FIELDS = 3,
JPEG = 4,
PNG = 5
}
public enum RasterOperations : uint
{
SOURCE_COPY = 0x00CC0020,
SOURCE_PAINT = 0x00EE0086,
SOURCE_AND = 0x008800C6,
SOURCE_INVERT = 0x00660046,
SOURCE_ERASE = 0x00440328,
NOT_SOURCE_COPY = 0x00330008,
NOT_SOURCE_ERASE = 0x001100A6,
MERGE_COPY = 0x00C000CA,
MERGE_PAINT = 0x00BB0226,
PATTERN_COPY = 0x00F00021,
PATTERN_PAINT = 0x00FB0A09,
PATTERN_INVERT = 0x005A0049,
DESTINATION_INVERT = 0x00550009,
BLACK_OUTPUT = 0x00000042,
WHITE_OUTPUT = 0x00FF0062,
CAPTURE_BIT = 0x40000000
}
public enum PrintWindowOptions : uint
{
[Description("Captures only the client area")]
CLIENT_AREA_ONLY = 0x00000001,
[Description("Captures DirectX/DirectComposition content")]
FULL_CONTENT_RENDER = 0x00000002
}
}
Win32Types Class
using System.Runtime.InteropServices;
public static class Win32Types
{
[StructLayout(LayoutKind.Sequential)]
public struct Coordinate
{
public int X;
public int Y;
public Coordinate(int x, int y)
{
X = x;
Y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Bounds
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public int Width => Right - Left;
public int Height => Bottom - Top;
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct BitmapFileHeader
{
public ushort FileType;
public uint FileSize;
public ushort Reserved1;
public ushort Reserved2;
public uint PixelDataOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct BitmapInfoHeader
{
public uint HeaderSize;
public int ImageWidth;
public int ImageHeight;
public ushort ColorPlanes;
public ushort BitsPerPixel;
public uint Compression;
public uint ImageSize;
public int HorizontalResolution;
public int VerticalResolution;
public uint ColorsUsed;
public uint ImportantColors;
public void Initialize()
{
HeaderSize = (uint)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RgbColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Reserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BitmapInformation
{
public BitmapInfoHeader Header;
public RgbColor Colors;
}
}
Win32Functions Class
using System;
using System.Runtime.InteropServices;
public static class Win32Functions
{
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr windowHandle, out Win32Types.Bounds bounds);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr windowHandle, out Win32Types.Bounds bounds);
[DllImport("user32.dll", EntryPoint = "GetWindowDC")]
public static extern IntPtr GetWindowDC(IntPtr windowHandle);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr deviceContext);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr deviceContext, int width, int height);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr deviceContext);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr windowHandle, IntPtr deviceContext);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateDIBSection(IntPtr deviceContext, ref Win32Types.BitmapInformation bitmapInfo,
uint colorUsage, out IntPtr pixelData, IntPtr fileMapping, uint offset);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr deviceContext, IntPtr graphicsObject);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr graphicsObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern bool BitBlt(
IntPtr destinationDC, int destX, int destY, int width, int height,
IntPtr sourceDC, int sourceX, int sourceY, uint rasterOperation);
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr windowHandle, IntPtr deviceContext, uint flags);
}
DIB Capture Implementation
DibCaptureHelper Class
using System;
public class DibCaptureHelper
{
public IntPtr BitmapHandle => bitmapHandle;
public Win32Types.BitmapInformation BitmapInfo => bitmapInfo;
public Win32Types.Bounds WindowBounds => windowBounds;
public Win32Types.Bounds ClientBounds => clientBounds;
public int PixelDataSize => pixelDataSize;
private IntPtr windowHandle = IntPtr.Zero;
private IntPtr sourceDC = IntPtr.Zero;
private IntPtr memoryDC = IntPtr.Zero;
private IntPtr bitmapHandle = IntPtr.Zero;
private IntPtr previousBitmap = IntPtr.Zero;
private IntPtr pixelDataPtr = IntPtr.Zero;
private Win32Types.BitmapInformation bitmapInfo;
private Win32Types.Bounds windowBounds;
private Win32Types.Bounds clientBounds;
private int pixelDataSize;
public bool Initialize(string targetWindowName)
{
var handle = Win32Functions.FindWindow(null, targetWindowName);
if (handle.Equals(IntPtr.Zero))
{
return false;
}
return Initialize(handle);
}
public bool Initialize(IntPtr targetHandle)
{
windowHandle = targetHandle;
if (!Win32Functions.GetWindowRect(windowHandle, out windowBounds) ||
!Win32Functions.GetClientRect(windowHandle, out clientBounds))
{
return false;
}
pixelDataSize = clientBounds.Width * clientBounds.Height * 3;
bitmapInfo = new Win32Types.BitmapInformation
{
Header = new Win32Types.BitmapInfoHeader()
};
bitmapInfo.Header.Initialize();
bitmapInfo.Header.ImageWidth = clientBounds.Width;
bitmapInfo.Header.ImageHeight = clientBounds.Height;
bitmapInfo.Header.ColorPlanes = 1;
bitmapInfo.Header.BitsPerPixel = 24;
bitmapInfo.Header.ImageSize = (uint)(clientBounds.Width * clientBounds.Height);
bitmapInfo.Header.Compression = (uint)Win32Constants.CompressionType.NONE;
sourceDC = Win32Functions.GetWindowDC(windowHandle);
memoryDC = Win32Functions.CreateCompatibleDC(sourceDC);
bitmapHandle = Win32Functions.CreateDIBSection(memoryDC, ref bitmapInfo,
(uint)Win32Constants.ColorTableUsage.RGB_COLORS,
out pixelDataPtr, IntPtr.Zero, 0);
previousBitmap = Win32Functions.SelectObject(memoryDC, bitmapHandle);
return true;
}
public void Cleanup()
{
if (bitmapHandle.Equals(IntPtr.Zero))
{
return;
}
Win32Functions.SelectObject(memoryDC, previousBitmap);
Win32Functions.DeleteObject(bitmapHandle);
Win32Functions.DeleteDC(memoryDC);
Win32Functions.ReleaseDC(windowHandle, sourceDC);
windowHandle = IntPtr.Zero;
sourceDC = IntPtr.Zero;
memoryDC = IntPtr.Zero;
bitmapHandle = IntPtr.Zero;
previousBitmap = IntPtr.Zero;
pixelDataPtr = IntPtr.Zero;
}
public bool RefreshWindow()
{
var currentHandle = windowHandle;
Cleanup();
return Initialize(currentHandle);
}
public bool ChangeWindowTarget(string newWindowName)
{
Cleanup();
return Initialize(newWindowName);
}
public bool ChangeWindowTarget(IntPtr newHandle)
{
Cleanup();
return Initialize(newHandle);
}
public IntPtr CaptureWindow()
{
if (bitmapHandle.Equals(IntPtr.Zero) || memoryDC.Equals(IntPtr.Zero) || sourceDC.Equals(IntPtr.Zero))
{
return IntPtr.Zero;
}
var success = Win32Functions.BitBlt(
memoryDC, 0, 0, clientBounds.Width, clientBounds.Height,
sourceDC, 0, 0,
(uint)Win32Constants.RasterOperations.SOURCE_COPY);
return success ? pixelDataPtr : IntPtr.Zero;
}
public bool CaptureWindow(out IntPtr pixelData, out int dataSize, out Win32Types.Bounds captureBounds)
{
pixelData = pixelDataPtr;
dataSize = pixelDataSize;
captureBounds = clientBounds;
if (bitmapHandle.Equals(IntPtr.Zero) || memoryDC.Equals(IntPtr.Zero) || sourceDC.Equals(IntPtr.Zero))
{
return false;
}
var success = Win32Functions.BitBlt(
memoryDC, 0, 0, clientBounds.Width, clientBounds.Height,
sourceDC, 0, 0,
(uint)Win32Constants.RasterOperations.SOURCE_COPY);
return success;
}
}