Ich versuche, eine Screenshot aller Bildschirme auf meinem PC zu erstellen. In der Vergangenheit habe ich die GDI-Methode verwendet, aber aufgrund von Leistungsproblemen versuche ich den DirectX-Weg.Erfassen aller Bildschirme mit DirectX GetFrontBufferData
ich einen Screenshot von einem einzigen Bildschirm ohne Probleme nehmen können, mit einem Code wie folgt aus:
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Windows.Forms;
using System.Drawing;
class Capture : Form
{
private Device device;
private Surface surface;
public Capture()
{
PresentParameters p = new PresentParameters();
p.Windowed = true;
p.SwapEffect = SwapEffect.Discard;
device = new Device(0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, p);
surface = device.CreateOffscreenPlainSurface(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, Format.A8B8G8R8, Pool.Scratch);
}
public Bitmap Frame()
{
GraphicsStream gs = SurfaceLoader.SaveToStream(ImageFileFormat.Jpg, surface);
return new Bitmap(gs);
}
}
(Lets ignorieren für diese Frage die Bitmap aus dem Speicher zu löschen)
Kodex Mit ich kann Mache einen Screenshot von meinem Hauptbildschirm. Das Ändern des ersten Parameters des Device
-Konstruktors auf eine andere Nummer entspricht einem anderen Bildschirm. Wenn ich 3 Bildschirme habe und 2
als Parameter übergebe, erhalte ich einen Screenshot von meinem dritten Bildschirm.
Das Problem, das ich habe, ist, wie mit der Erfassung aller Bildschirme umzugehen. Ich kam mit dem Follow-up:
class CaptureScreen : Form
{
private int index;
private Screen screen;
private Device device;
private Surface surface;
public Rectangle ScreenBounds { get { return screen.Bounds; } }
public Device Device { get { return device; } }
public CaptureScreen(int index, Screen screen, PresentParameters p)
{
this.screen = screen; this.index = index;
device = new Device(index, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, p);
surface = device.CreateOffscreenPlainSurface(screen.Bounds.Width, screen.Bounds.Height, Format.A8R8G8B8, Pool.Scratch);
}
public Bitmap Frame()
{
device.GetFrontBufferData(0, surface);
GraphicsStream gs = SurfaceLoader.SaveToStream(ImageFileFormat.Jpg, surface);
return new Bitmap(gs);
}
}
class CaptureDirectX : Form
{
private CaptureScreen[] screens;
private int width = 0;
private int height = 0;
public CaptureDirectX()
{
PresentParameters p = new PresentParameters();
p.Windowed = true;
p.SwapEffect = SwapEffect.Discard;
screens = new CaptureScreen[Screen.AllScreens.Length];
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
screens[i] = new CaptureScreen(i, Screen.AllScreens[i], p);
//reset previous devices
if (i > 0)
{
for(int j = 0; j < i; j++)
{
screens[j].Device.Reset(p);
}
}
width += Screen.AllScreens[i].Bounds.Width;
if (Screen.AllScreens[i].Bounds.Height > height)
{
height = Screen.AllScreens[i].Bounds.Height;
}
}
}
public Bitmap Frame()
{
Bitmap result = new Bitmap(width, height);
using (var g = Graphics.FromImage(result))
{
for (int i = 0; i < screens.Length; i++)
{
Bitmap frame = screens[i].Frame();
g.DrawImage(frame, screens[i].Bounds);
}
}
return result;
}
}
Wie Sie sehen können, ich aber die verfügbaren Bildschirme durchlaufen und mehrere Geräte und Oberflächen in einer separaten Klasse erstellen. Aber Aufruf Frame()
der CaptureDirectX
Klasse führt den folgenden Fehler:
An unhandled exception of type 'Microsoft.DirectX.Direct3D.InvalidCallException' occurred in Microsoft.DirectX.Direct3D.dll
Auf der Linie
device.GetFrontBufferData(0, surface);
Ich habe diese ein wenig erforscht, aber hatte nicht viel Erfolg. Ich bin mir nicht sicher, was das Problem ist. Ich habe eine link gefunden, die eine Lösung bietet, die über das Zurücksetzen der Device
Objekte spricht. Aber wie Sie in meinem Code oben sehen können, habe ich versucht, alle zuvor erstellten Device
Objekte zurückzusetzen, leider ohne Erfolg.
Also meine Fragen sind:
- Ist das, was ich sogar möglich, durch diese Methode (das heißt GetFrontBufferData) zu erreichen bin versucht?
- Was mache ich falsch? Was vermisse ich?
- Sehen Sie Leistungsprobleme bei der Aufnahme des Bildschirms mit hoher Geschwindigkeit, sagen wir 30 fps? (Einen einzigen Bildschirm mit einem Ziel von 30 Bildern pro Sekunde Capturing gab mir eine Geschwindigkeit von etwa 25 - 30 Bildern pro Sekunde, verglichen mit dem GDI methology der 15fps manchmal gerne sinkt)
FYI es ist eine WPF-Anwendung, dh .NET 4.5
Edit: Ich sollte erwähnen, dass ich IDXGI_DesktopDuplication
bewusst bin, aber leider nicht meinen Anforderungen entspricht. Soweit ich weiß, ist diese API nur ab Windows 8 verfügbar, aber ich versuche, eine Lösung zu finden, die aufgrund meiner Clients ab Windows 7 funktioniert.
https://StackOverflow.com/Questions/25681915/c-direct3d-multiple-screen-capture – VuVirt
@VuVirt Ja, ich habe diese Frage gesehen, aber seine Lösung funktioniert nicht für mich. Ich kann mehrere Geräte problemlos erstellen, aber den Frontpuffer von ihnen zu bekommen, ist das Problem. – DodgerThud
Ich denke, Sie sollten GetFrontBufferData für jeden Bildschirm (korrekte Korrektoren vorausgesetzt) separat aufrufen. – VuVirt