Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用DirectX GetFrontBufferData捕获所有屏幕_C#_.net_Directx - Fatal编程技术网

C# 使用DirectX GetFrontBufferData捕获所有屏幕

C# 使用DirectX GetFrontBufferData捕获所有屏幕,c#,.net,directx,C#,.net,Directx,我正在尝试在我的电脑上创建所有屏幕的屏幕截图。过去我一直使用GDI方法,但由于性能问题,我尝试使用DirectX方法 我可以拍摄一个没有问题的屏幕截图,代码如下: using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using System.Windows.Forms; using System.Drawing; class Capture : Form { private Device device;

我正在尝试在我的电脑上创建所有屏幕的屏幕截图。过去我一直使用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);
    }
}
(对于此问题,忽略从内存中删除位图)

有了这些代码,我可以拍摄我的主屏幕截图。将
设备
构造函数的第一个参数更改为不同的数字对应于不同的屏幕。如果我有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;
    }
}
我对此进行了一些研究,但没有取得很大的成功。我真的不确定问题出在哪里。 我发现了一个解决方案,它提供了重置
设备
对象的解决方案。但正如您在我上面的代码中所看到的,我一直在尝试重置所有以前创建的
设备
对象,可惜没有成功

因此,我的问题是:

  • 我试图通过这种方法(即GetFrontBufferData)实现的目标可能实现吗
  • 我做错了什么?我错过了什么
  • 在以高速率(比如每秒30帧)捕获屏幕时,您是否看到任何性能问题?(拍摄一个目标为30fps的单屏时,我的速度约为25-30fps,而GDI方法有时会下降到15fps左右)
仅供参考,这是一个WPF应用程序,即NET4.5


编辑:我应该提到,我知道
IDXGI\u desktopreplication
,但遗憾的是,它不符合我的要求。据我所知,该API仅在Windows 8以后可用,但由于我的客户端,我正试图获得一个从Windows 7以后可用的解决方案。

好吧,最终解决方案是完全不同的。
System.Windows.Forms.Screen
类不能很好地与
DirectX
类配合使用。为什么?因为索引不匹配。
AllScreens
中的第一个对象不必是
设备安装中的索引0

现在通常这不是问题,除非你有一个像我这样的“奇怪的”监视器设置。在桌子上我有三个屏幕,一个垂直(12001920),一个水平(19201200)和另一个水平笔记本电脑屏幕(19201080)

在我的例子中发生了什么:
AllScreens
中的第一个对象是左侧的垂直监视器。我尝试为索引0、1200宽和1920高创建一个设备。索引0对应于我的主监视器,即中间的水平监视器。所以我的Instantiation基本上超出了屏幕范围。Instantiation不会抛出异常,稍后我会尝试读取前端缓冲区数据。Bam,例外,因为我正在尝试拍摄一个1200x1920屏幕截图,屏幕截图是1920x1200

可悲的是,即使在我完成这项工作后,表演也不好。所有3个监视器的单个帧大约需要300到500毫秒。即使只有一个监视器,执行时间也大约为100毫秒。对于我的用例来说还不够好。 也没有让Backbuffer工作,它只是生成黑色图像


我回到GDI方法,并通过在每次调用
Frame()
时只更新位图的特定块来增强它。你想捕捉一个1920x1200的区域,它被切割成480x300个矩形。

@VuVirt是的,我见过这个问题,但他的解决方案对我不起作用。我可以很好地创建多个设备,但问题是如何从它们获取前端缓冲区。我认为您应该分别为每个屏幕调用GetFrontBufferData(提供正确的坐标)。@VuVirt这不是我正在做的吗?我通过
CaptureScreen
对象进行迭代,每个对象都有自己的
Device
Surface
分配。尽管根据我发布的链接,注册第二个设备实际上会破坏我之前创建的设备。你在每次迭代中都会创建一个新设备。在下一个设备创建之前,旧设备似乎不会被销毁。在screens[i]=new CaptureScreen(i,Screen.AllScreens[i],p)创建新设备之前,您可能需要在所有屏幕上执行screens[j].Device.Dispose;您可能还需要在每次Dispose之后或创建新设备之前调用GC.Collect。
device.GetFrontBufferData(0, surface);