我正在尝试在我的电脑上创建所有屏幕的屏幕截图。在过去,我一直在使用GDI方法,但由于性能问题,我正在尝试DirectX方式。
我可以在没有问题的情况下拍摄单个屏幕的屏幕截图,代码如下:
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;
}
}
如您所见,我迭代可用的屏幕并在单独的类中创建多个设备和表面。但是调用Frame()
类的CaptureDirectX
会引发以下错误:
Microsoft.DirectX.Direct3D.dll中出现“Microsoft.DirectX.Direct3D.InvalidCallException”类型的未处理异常
在
行device.GetFrontBufferData(0, surface);
我一直在研究这一点,但没有取得很大的成功。我不确定问题是什么。
我找到了一个link,提供了一个解决方案,即重置Device
对象。但正如您在上面的代码中所看到的,我一直在尝试重置所有以前创建的Device
对象,遗憾的是没有成功。
所以我的问题是:
仅供参考,它是一个WPF应用程序,即.NET 4.5
编辑:我应该提到我知道IDXGI_DesktopDuplication
但遗憾的是它不符合我的要求。据我所知,该API只能从Windows 8开始提供,但我试图从我的客户那里获得一个可以从Windows 7开始运行的解决方案。
答案 0 :(得分:0)
嗯,最终解决方案完全不同。 System.Windows.Forms.Screen
班级与DirectX
班级没有很好的配合。为什么?因为索引不匹配。 AllScreens
中的第一个对象不一定必须是Device
instatiation中的索引0。
现在通常这不是一个问题,除非你有一个&#34;奇怪的&#34;像我一样监控设置。在桌子上我有3个屏幕,一个垂直(1200,1920),一个水平(1920,1200)和另一个水平笔记本电脑屏幕(1920,1080)。
我的情况发生了什么:AllScreens
中的第一个对象是左侧的垂直监视器。我尝试为索引0,1200宽度和1920高度创建一个设备。索引0对应于我的主监视器,即中间的水平监视器。因此,我实际上已经离开屏幕界限了我的实例。 instatiation不会抛出异常,稍后我会尝试读取前缓冲区数据。 Bam,例外,因为我试图拍摄1920x1200的1920x1200的屏幕截图。
我回到GDI方法并通过仅在每个Frame()
调用上更新位图的特定块来增强它。您想捕获1920x1200区域,该区域被切割成480x300矩形。