我正試圖在PC上創建所有屏幕的屏幕截圖。在過去,我一直在使用GDI方法,但由於性能問題,我正在嘗試DirectX方式。使用DirectX捕獲所有屏幕GetFrontBufferData
我可以採取截圖的單一屏幕沒有問題,用這樣的代碼:
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);
}
}
(讓我們忽略刪除從內存中的位圖這個問題)
與該代碼我可以採取我的主要屏幕的屏幕截圖。將Device
構造函數的第一個參數更改爲不同的數字對應於不同的屏幕。如果我有3個屏幕,並且我通過2
作爲參數,我會看到我的第三個屏幕的屏幕截圖。
我遇到的問題是如何處理捕獲所有屏幕。我想出了以下幾點:
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;
}
}
正如你所看到的,我遍歷可用的屏幕並在一個單獨的類中創建多個設備和曲面。但在調用CaptureDirectX
類的Frame()
引發以下錯誤:
An unhandled exception of type 'Microsoft.DirectX.Direct3D.InvalidCallException' occurred in Microsoft.DirectX.Direct3D.dll
在生產線
device.GetFrontBufferData(0, surface);
我一直在研究這個有點,但沒有一大堆的成功。我不確定問題是什麼。 我找到了一個link,它提供了一個解決方案,用於重置Device
對象。但正如您在上面的代碼中看到的,我一直在嘗試重置以前創建的所有對象,遺憾的是沒有成功。
所以我的問題是:
- 是什麼,我想通過這個方法(即GetFrontBufferData)實現甚至可能嗎?
- 我在做什麼錯?我錯過了什麼?
- 當您以高速率捕捉屏幕時,您是否看到任何性能問題,比如說30 fps? (拍攝有30fps的的目標單一的屏幕給了我25的速度 - 每秒30幀,與GDI計量學下沉有時喜歡15fps的比較)
FYI這是一個WPF應用程序,即.NET 4.5
編輯:我應該提到,我知道IDXGI_DesktopDuplication
,但可悲的是,它不符合我的要求。據我所知,該API僅在Windows 8以上版本中可用,但我試圖從我的客戶端獲得Windows 7以上的解決方案。
https://stackoverflow.com/questions/25681915/c-direct3d-multiple-screen-capture – VuVirt
@VuVirt是的,我已經看到了這個問題,但他的解決方案並不適合我。我可以創建多個設備,但從它們獲取前端緩衝區是問題所在。 – DodgerThud
我認爲你應該爲每個屏幕(提供正確的座標)分別調用GetFrontBufferData。 – VuVirt