Files
CPF/CPF.Windows/ScreenImpl.cs

156 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using CPF.Drawing;
using CPF.Platform;
using static CPF.Windows.UnmanagedMethods;
namespace CPF.Windows
{
public class ScreenImpl : Screen
{
private readonly IntPtr _hMonitor;
public ScreenImpl(Rect bounds, Rect workingArea, bool primary, IntPtr hMonitor) : base(bounds, workingArea, primary)
{
this._hMonitor = hMonitor;
}
//public IReadOnlyList<Screen> GetAllScreens()
//{
// var ScreenCount = GetSystemMetrics(SystemMetric.SM_CMONITORS);
// List<Screen> screens = new List<Screen>();
// EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
// (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) =>
// {
// screens.Add(FromMonitor(monitor, hdcMonitor));
// return true;
// }, IntPtr.Zero);
// return screens;
//}
public static Screen FromMonitor(IntPtr monitor, IntPtr hdc)
{
MONITORINFO monitorInfo = MONITORINFO.Create();
if (GetMonitorInfo(monitor, ref monitorInfo))
{
RECT bounds = monitorInfo.rcMonitor;
RECT workingArea = monitorInfo.rcWork;
Rect cBounds = new Rect(bounds.left, bounds.top, bounds.right - bounds.left,
bounds.bottom - bounds.top);
Rect cWorkArea =
new Rect(workingArea.left, workingArea.top, workingArea.right - workingArea.left,
workingArea.bottom - workingArea.top);
return new ScreenImpl(cBounds, cWorkArea, monitorInfo.dwFlags == 1,
monitor);
}
return null;
}
public override int GetHashCode()
{
return (int)_hMonitor;
}
public override bool Equals(object obj)
{
return (obj is ScreenImpl screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj);
}
public override Bitmap Screenshot()
{
IntPtr desktopHwnd = GetDesktopWindow();
IntPtr desktopDc = GetWindowDC(desktopHwnd);
IntPtr memDc = CreateCompatibleDC(desktopDc);
try
{
// 3. 创建DIBSection (24位真彩色位图)
BITMAPINFOHEADER bmi = new BITMAPINFOHEADER
{
biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)),
biWidth = (int)Bounds.Width,
biHeight = -(int)Bounds.Height, // 负值表示自顶向下DIB
biPlanes = 1,
biBitCount = 24,
biCompression = 0, // BI_RGB
biSizeImage = (uint)(((int)Bounds.Width * 3 + 3) & ~3) * (uint)Bounds.Height // 行对齐到4字节
}
;
IntPtr hBitmap = CreateDIBSection(desktopDc, ref bmi, 0, out var bitsPtr, IntPtr.Zero, 0);
// 4. 将屏幕内容复制到位图
IntPtr oldBitmap = SelectObject(memDc, hBitmap);
BitBlt(memDc, 0, 0, (int)Bounds.Width, (int)Bounds.Height, desktopDc, 0, 0, TernaryRasterOperations.SRCCOPY | TernaryRasterOperations.CAPTUREBLT);
SelectObject(memDc, oldBitmap);
// 5. 构造BMP文件
using (MemoryStream ms = new MemoryStream())
{
// 5.1 计算文件大小
uint pixelDataSize = bmi.biSizeImage;
uint fileSize = (uint)(Marshal.SizeOf(typeof(BITMAPFILEHEADER)) +
(Marshal.SizeOf(typeof(BITMAPINFOHEADER))) +
pixelDataSize);
// 5.2 创建文件头
BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER
{
bfType = 0x4D42, // "BM"
bfSize = fileSize,
bfOffBits = (uint)(Marshal.SizeOf(typeof(BITMAPFILEHEADER)) +
(Marshal.SizeOf(typeof(BITMAPINFOHEADER))))
}
;
// 5.3 写入文件头
byte[] fileHeaderBytes = StructureToBytes(fileHeader);
ms.Write(fileHeaderBytes, 0, fileHeaderBytes.Length);
// 5.4 写入信息头
byte[] infoHeaderBytes = StructureToBytes(bmi);
ms.Write(infoHeaderBytes, 0, infoHeaderBytes.Length);
// 5.5 写入像素数据 (直接从DIBSection内存复制)
byte[] pixelData = new byte[pixelDataSize];
Marshal.Copy(bitsPtr, pixelData, 0, (int)pixelDataSize);
ms.Write(pixelData, 0, pixelData.Length);
ms.Position = 0;
Bitmap bitmap = new Bitmap(ms);
return bitmap;
}
}
finally
{
// 6. 清理资源
ReleaseDC(desktopHwnd, desktopDc);
DeleteDC(memDc);
}
}
// 辅助方法:结构体转字节数组
private static byte[] StructureToBytes<T>(T structure) where T : struct
{
int size = Marshal.SizeOf(typeof(T));
byte[] bytes = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structure, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
}
}