Как сделать скриншота экрана и окна с помощью DirectX C#?
Нужно сделать скриншот экрана и окна на C# и сохранить в любом виде в коде(Не в файл). Пробывал сделать через GDI.
public static Bitmap CaptureWindow(IntPtr hwnd)
{
IntPtr windowPtr = hwnd;
Rect windowRect = new Rect();
WindowsManager.GetWindowRect(windowPtr, ref windowRect);
Rectangle bounds = new Rectangle(windowRect.Left, windowRect.Top, windowRect.Right - windowRect.Left, windowRect.Bottom - windowRect.Top);
Bitmap b = new Bitmap(bounds.Width, bounds.Height);
using (Graphics g = Graphics.FromImage(b))
{
WindowsManager.PrintWindow(windowPtr, g.GetHdc(), 0);
}
return b;
}
Но данным способом нельзя сделать скриншот окна которое рисуется не виндой. К тому же способ очень медленный как я знаю. Поэтому решил сделать через DirectX но в нём ничего не шарю, а информации не столь много по этой теме.
Ответы (1 шт):
Автор решения: aepot
→ Ссылка
- Создаю .NET 6 консольное приложение
- Ставлю NuGet пакеты: System.Drawing.Common, SharpDX, SharpDX.DXGI, SharpDX.Direct3D11
Пишу вот такой код
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
namespace DXScreenshot;
class Program
{
static void Main(string[] args)
{
string path = "screenshot.png";
using var bmp = TakeScreenshot();
bmp.Save(path, ImageFormat.Png);
Process.Start(new ProcessStartInfo { FileName = path, UseShellExecute = true });
}
static Bitmap TakeScreenshot()
{
var factory = new Factory1();
var adapter = factory.GetAdapter1(0);
Console.WriteLine(adapter.Description1.Description);
var device = new SharpDX.Direct3D11.Device(adapter);
Output output = adapter.GetOutput(0);
Console.WriteLine(output.Description.DeviceName);
Output1 output1 = output.QueryInterface<Output1>();
int width = output.Description.DesktopBounds.Right;
int height = output.Description.DesktopBounds.Bottom;
Texture2DDescription textureDesc = new Texture2DDescription
{
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Width = width,
Height = height,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging
};
using Texture2D screenTexture = new Texture2D(device, textureDesc);
using OutputDuplication duplicatedOutput = output1.DuplicateOutput(device);
Thread.Sleep(20); // захватчику экрана надо время проинициализироваться
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
SharpDX.DXGI.Resource screenResource = null;
try
{
if (duplicatedOutput.TryAcquireNextFrame(10, out OutputDuplicateFrameInformation duplicateFrameInformation, out screenResource) != Result.Ok)
return bmp;
using (Texture2D screenTexture2D = screenResource.QueryInterface<Texture2D>())
{
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
}
DataBox mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, bmp.PixelFormat);
nint sourcePtr = mapSource.DataPointer;
nint destPtr = bmpData.Scan0;
Utilities.CopyMemory(destPtr, sourcePtr, mapSource.RowPitch * height);
bmp.UnlockBits(bmpData);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
duplicatedOutput.ReleaseFrame();
}
catch (SharpDXException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
screenResource?.Dispose();
}
return bmp;
}
}
Запускаю, и картинка на экране.
Несмотря на Thread.Sleep это очень быстрый способ делать скрины. На самом деле OutputDuplication можно проинициализировать однократно, а скриншотов можно делать потом с него сколько угодно и очень быстро, хоть видео с экрана писать можно.
