Last active
August 10, 2017 02:36
-
-
Save azyobuzin/2eb3379397e9db7336687723429084e7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
namespace ConsoleApp2.WindowsApi | |
{ | |
public abstract class DCHandle : SafeHandle | |
{ | |
public DCHandle(bool ownsHandle) | |
: base(IntPtr.Zero, ownsHandle) | |
{ } | |
public DCHandle() : this(true) { } | |
public override bool IsInvalid => this.handle == IntPtr.Zero; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
namespace ConsoleApp2.WindowsApi | |
{ | |
public enum BitmapCompression : uint | |
{ | |
BI_RGB = 0, | |
BI_RLE8 = 1, | |
BI_RLE4 = 2, | |
BI_BITFIELDS = 3, | |
BI_JPEG = 4, | |
BI_PNG = 5 | |
} | |
public enum DibColorTableIdentifier : uint | |
{ | |
DIB_RGB_COLORS = 0, | |
DIB_PAL_COLORS = 1 | |
} | |
public enum RasterOperationCode : 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, | |
NOMIRRORBITMAP = 0x80000000, | |
CAPTUREBLT = 0x40000000 | |
} | |
public class BitmapHandle : SafeHandle | |
{ | |
public BitmapHandle() : base(IntPtr.Zero, true) | |
{ } | |
public override bool IsInvalid => this.handle == IntPtr.Zero; | |
protected override bool ReleaseHandle() | |
{ | |
return Gdi32.DeleteObject(this.handle); | |
} | |
} | |
public class GdiDCHandle : DCHandle | |
{ | |
protected override bool ReleaseHandle() | |
{ | |
return Gdi32.DeleteDC(this.handle); | |
} | |
} | |
public struct BitmapInfoHeader | |
{ | |
public int Width; | |
public int Height; | |
public ushort Planes; | |
public ushort BitCount; | |
public BitmapCompression Compression; | |
public uint SizeImage; | |
public int XPelsPerMeter; | |
public int YPelsPerMeter; | |
public uint ClrUsed; | |
public uint ClrImportant; | |
} | |
public struct RgbQuad | |
{ | |
public byte Blue; | |
public byte Green; | |
public byte Red; | |
public byte Reserved; | |
} | |
public class BitmapInfo | |
{ | |
public BitmapInfoHeader Header { get; set; } | |
public IReadOnlyList<RgbQuad> Colors { get; set; } | |
internal unsafe byte[] ToBytes() | |
{ | |
var colorCount = this.Colors?.Count ?? 0; | |
var colorsBytes = (colorCount <= 0 ? 1 : colorCount) * sizeof(RgbQuad); | |
var bs = new byte[ | |
sizeof(uint) // biSize | |
+ sizeof(BitmapInfoHeader) // bmiHeader | |
+ colorsBytes // bmiColors | |
]; | |
fixed (byte* ptr = bs) | |
{ | |
*(uint*)ptr = (uint)(sizeof(uint) + sizeof(BitmapInfoHeader)); // biSize | |
*(BitmapInfoHeader*)(ptr + sizeof(uint)) = this.Header; // other fields | |
var dstColors = (RgbQuad*)(ptr + sizeof(uint) + sizeof(BitmapInfoHeader)); | |
if (colorCount >= 1) | |
{ | |
if (this.Colors is RgbQuad[] arrColors) | |
{ | |
fixed (RgbQuad* srcColors = arrColors) | |
{ | |
Buffer.MemoryCopy(srcColors, dstColors, colorsBytes, arrColors.Length * sizeof(RgbQuad)); | |
} | |
} | |
else | |
{ | |
for (var i = 0; i < colorCount; i++) | |
{ | |
dstColors[i] = this.Colors[i]; | |
} | |
} | |
} | |
} | |
return bs; | |
} | |
internal unsafe void Return(byte[] bs) | |
{ | |
fixed (byte* ptr = bs) | |
{ | |
this.Header = *(BitmapInfoHeader*)(ptr + sizeof(uint)); | |
} | |
} | |
} | |
public static class Gdi32 | |
{ | |
public const string DllName = "gdi32"; | |
[DllImport(DllName, EntryPoint = "BitBlt", ExactSpelling = true, SetLastError = true)] | |
private static extern bool _BitBlt(DCHandle hdc, int x, int y, int cx, int cy, DCHandle hdcSrc, int x1, int y1, RasterOperationCode rop); | |
public static void BitBlt(DCHandle hdc, int x, int y, int cx, int cy, DCHandle hdcSrc, int x1, int y1, RasterOperationCode rop) | |
{ | |
if (!_BitBlt(hdc, x, y, cx, cy, hdcSrc, x1, y1, rop)) | |
throw new Win32Exception(); | |
} | |
[DllImport(DllName, EntryPoint = "CreateCompatibleBitmap", ExactSpelling = true)] | |
private static extern BitmapHandle _CreateCompatibleBitmap(DCHandle hdc, int cx, int cy); | |
public static BitmapHandle CreateCompatibleBitmap(DCHandle hdc, int cx, int cy) | |
{ | |
var result = _CreateCompatibleBitmap(hdc, cx, cy); | |
if (result.IsInvalid) throw new Exception(); | |
return result; | |
} | |
[DllImport(DllName, EntryPoint = "CreateCompatibleDC", ExactSpelling = true)] | |
private static extern GdiDCHandle _CreateCompatibleDC(DCHandle hdc); | |
public static GdiDCHandle CreateCompatibleDC(DCHandle hdc) | |
{ | |
var result = _CreateCompatibleDC(hdc); | |
if (result.IsInvalid) throw new Exception(); | |
return result; | |
} | |
[DllImport(DllName, ExactSpelling = true)] | |
internal static extern bool DeleteDC(IntPtr hdc); | |
[DllImport(DllName, ExactSpelling = true)] | |
internal static extern bool DeleteObject(IntPtr ho); | |
[DllImport(DllName, EntryPoint = "GetDIBits", ExactSpelling = true)] | |
private static extern int _GetDIBits(DCHandle hdc, BitmapHandle hbm, uint start, uint cLines, [Out] byte[] lpvBits, [In, Out] byte[] lpbmi, DibColorTableIdentifier usage); | |
public static int GetDIBits(DCHandle hdc, BitmapHandle hbm, uint start, uint cLines, byte[] lpvBits, BitmapInfo lpbmi, DibColorTableIdentifier usage) | |
{ | |
var bsBitmapInfo = lpbmi.ToBytes(); | |
var result = _GetDIBits(hdc, hbm, start, cLines, lpvBits, bsBitmapInfo, usage); | |
lpbmi.Return(bsBitmapInfo); | |
if (result == 0) throw new Exception(); | |
return result; | |
} | |
[DllImport(DllName, ExactSpelling = true)] | |
public static extern IntPtr SelectObject(DCHandle hdc, IntPtr h); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Threading; | |
using ConsoleApp2.WindowsApi; | |
using ImageSharp; | |
using ImageSharp.PixelFormats; | |
using SixLabors.Primitives; | |
namespace ConsoleApp2 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var p = Process.Start(@"notepad.exe"); | |
Console.WriteLine(p.Id); | |
Thread.Sleep(1500); | |
var hwnd = FindMainWindow(p.Id); | |
var rect = User32.GetClientRect(hwnd); | |
var size = new Size(rect.Right, rect.Bottom); | |
byte[] buf; | |
using (var windowDC = User32.GetDC(hwnd)) | |
using (var hBitmap = Gdi32.CreateCompatibleBitmap(windowDC, size.Width, size.Height)) | |
using (var bitmapDC = Gdi32.CreateCompatibleDC(windowDC)) | |
{ | |
if (Gdi32.SelectObject(bitmapDC, hBitmap.DangerousGetHandle()) == IntPtr.Zero) | |
throw new Exception(); | |
Gdi32.BitBlt(bitmapDC, 0, 0, size.Width, size.Height, windowDC, 0, 0, RasterOperationCode.SRCCOPY); | |
var bmi = new BitmapInfo() | |
{ | |
Header = new BitmapInfoHeader() | |
{ | |
Width = size.Width, | |
Height = size.Height, | |
Planes = 1, | |
BitCount = 32, | |
Compression = BitmapCompression.BI_RGB, | |
SizeImage = 0, | |
XPelsPerMeter = 0, | |
YPelsPerMeter = 0, | |
ClrUsed = 0, | |
ClrImportant = 0 | |
} | |
}; | |
buf = new byte[size.Width * size.Height * 4]; | |
Gdi32.GetDIBits(windowDC, hBitmap, 0, (uint)size.Height, buf, bmi, DibColorTableIdentifier.DIB_RGB_COLORS); | |
} | |
using (var image = new Image<Argb32>(size.Width, size.Height)) | |
{ | |
var bytesPerLine = size.Width * 4; | |
var bufSpan = new ReadOnlySpan<byte>(buf); | |
var pxSpan = image.Pixels.AsBytes(); | |
for (var line = 0; line < size.Height; line++) | |
{ | |
bufSpan.Slice(buf.Length - (line + 1) * bytesPerLine, bytesPerLine) | |
.CopyTo(pxSpan.Slice(line * bytesPerLine, bytesPerLine)); | |
} | |
image.Save("output.png"); | |
} | |
Console.WriteLine("completed"); | |
} | |
public static IntPtr FindMainWindow(int processId) | |
{ | |
var result = IntPtr.Zero; | |
User32.EnumWindows( | |
(hWnd, lParam) => | |
{ | |
User32.GetWindowThreadProcessId(hWnd, out var pid); | |
if (pid == processId | |
&& User32.GetWindow(hWnd, GetWindowCmd.GW_OWNER) == IntPtr.Zero | |
&& User32.IsWindowVisible(hWnd)) | |
{ | |
result = hWnd; | |
return false; | |
} | |
return true; | |
}, | |
IntPtr.Zero | |
); | |
return result; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
namespace ConsoleApp2.WindowsApi | |
{ | |
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam); | |
public enum GetWindowCmd : uint | |
{ | |
GW_HWNDFIRST, | |
GW_HWNDLAST, | |
GW_HWNDNEXT, | |
GW_HWNDPREV, | |
GW_OWNER, | |
GW_CHILD, | |
GW_ENABLEDPOPUP | |
} | |
public class BorrowedDCHandle : DCHandle | |
{ | |
public IntPtr Hwnd { get; } | |
public BorrowedDCHandle(IntPtr hwnd, IntPtr hdc) | |
{ | |
this.Hwnd = hwnd; | |
this.SetHandle(hdc); | |
} | |
protected override bool ReleaseHandle() | |
{ | |
return User32.ReleaseDC(this.Hwnd, this.handle) == 1; | |
} | |
} | |
public struct Rect | |
{ | |
public int Left; | |
public int Top; | |
public int Right; | |
public int Bottom; | |
} | |
public static class User32 | |
{ | |
public const string DllName = "user32"; | |
[DllImport(DllName, EntryPoint = "EnumWindows", ExactSpelling = true, SetLastError = true)] | |
private static extern bool _EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); | |
public static bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam) | |
{ | |
var result = _EnumWindows(lpEnumFunc, lParam); | |
if (!result) | |
{ | |
var error = Marshal.GetLastWin32Error(); | |
if (error != 0) throw new Win32Exception(); | |
} | |
return result; | |
} | |
[DllImport(DllName, EntryPoint = "GetClientRect", ExactSpelling = true, SetLastError = true)] | |
private static extern bool _GetClientRect(IntPtr hWnd, out Rect lpRect); | |
public static Rect GetClientRect(IntPtr hWnd) | |
{ | |
return _GetClientRect(hWnd, out var rect) | |
? rect | |
: throw new Win32Exception(); | |
} | |
[DllImport(DllName, EntryPoint = "GetDC", ExactSpelling = true)] | |
private static extern IntPtr _GetDC(IntPtr hWnd); | |
public static BorrowedDCHandle GetDC(IntPtr hWnd) | |
{ | |
var result = _GetDC(hWnd); | |
if (result == IntPtr.Zero) throw new Exception(); | |
return new BorrowedDCHandle(hWnd, result); | |
} | |
[DllImport(DllName, EntryPoint = "GetWindow", ExactSpelling = true, SetLastError = true)] | |
private static extern IntPtr _GetWindow(IntPtr hWnd, GetWindowCmd uCmd); | |
public static IntPtr GetWindow(IntPtr hWnd, GetWindowCmd uCmd, bool throwIfNull = false) | |
{ | |
var result = _GetWindow(hWnd, uCmd); | |
if (result == IntPtr.Zero && throwIfNull) | |
throw new Win32Exception(); | |
return result; | |
} | |
[DllImport(DllName, ExactSpelling = true)] | |
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); | |
[DllImport(DllName, ExactSpelling = true)] | |
public static extern bool IsWindowVisible(IntPtr hWnd); | |
[DllImport(DllName, ExactSpelling = true)] | |
internal static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment