截图并使用 SharpDX 在 Windows 表单上绘制

Take screenshot and draw it on a Windows Form using SharpDX

我正在尝试使用 SharpDX 捕获屏幕截图并将其显示在 Windows 表单(或面板)上,但我遇到了一些麻烦。我使用 OutputDuplication.AcquireNextFrame 捕获屏幕截图,但是当我尝试使用 WindowRenderTarget.DrawBitmap 绘制捕获的帧时,它失败并显示 WrongResourceDomain 例外。我添加了一个表单并修改了 ScreenCapture sample 的代码来说明我的问题:

using System;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D11;
using SharpDX.DXGI;

namespace MiniTri
{
    /// <summary>
    ///   Screen capture of the desktop using DXGI OutputDuplication.
    /// </summary>
    internal static class Program
    {

        [STAThread]
        private static void Main()
        {
            var Form1 = new ScreenCapture.Form1();

            // # of graphics card adapter
            const int numAdapter = 0;

            // # of output device (i.e. monitor)
            const int numOutput = 0;

            // Create DXGI Factory1
            var dxgiFactory = new SharpDX.DXGI.Factory1();
            var dxgiAdapter = dxgiFactory.GetAdapter1(numAdapter);

            // Create device from Adapter
            var d3dDevice = new SharpDX.Direct3D11.Device(dxgiAdapter, DeviceCreationFlags.BgraSupport);

            // Create Direct2D1 Factory1
            var d2dFactory = new SharpDX.Direct2D1.Factory1();

            // Create Direct2D device
            SharpDX.Direct2D1.Device d2dDevice;
            using (var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device>())
                d2dDevice = new SharpDX.Direct2D1.Device(d2dFactory, dxgiDevice);

            // Get the Direct2D1 DeviceContext
            var d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, DeviceContextOptions.None);

            // Create Direct2D1 WindowRenderTarget
            var WindowRenderTarget = new WindowRenderTarget(d2dFactory,
                                                            new RenderTargetProperties
                                                            {
                                                                Type = RenderTargetType.Default
                                                            },
                                                            new HwndRenderTargetProperties
                                                            {
                                                                Hwnd = Form1.Handle,
                                                                PixelSize = new Size2(Form1.Width, Form1.Height),
                                                                PresentOptions = PresentOptions.Immediately
                                                            });

            // Get DXGI.Output
            var output = dxgiAdapter.GetOutput(numOutput);
            var output1 = output.QueryInterface<Output1>();

            // Create RawRectangleF with the Form size
            var FormRectangle = new SharpDX.Mathematics.Interop.RawRectangleF(0, 0, Form1.Width, Form1.Height);

            // Duplicate the output
            var duplicatedOutput = output1.DuplicateOutput(d3dDevice);

            bool captureDone = false;
            for (int i = 0; !captureDone; i++)
            {
                try
                {
                    SharpDX.DXGI.Resource screenResource;
                    OutputDuplicateFrameInformation duplicateFrameInformation;

                    // Try to get duplicated frame within given time
                    duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out screenResource);

                    if (i > 0)
                    {
                        // Create a Direct2D1 Bitmap1 from screenResource
                        var dxgiSurface = screenResource.QueryInterface<Surface>();
                        var d2dBitmap1 = new Bitmap1(d2dContext, dxgiSurface);

                        // Should draw the bitmap on the form, but fails with "WrongResourceDomain" exception
                        WindowRenderTarget.BeginDraw();
                        WindowRenderTarget.DrawBitmap(d2dBitmap1, FormRectangle, 1.0f, BitmapInterpolationMode.Linear);
                        WindowRenderTarget.EndDraw();

                        // Capture done
                        captureDone = true;
                    }

                    screenResource.Dispose();
                    duplicatedOutput.ReleaseFrame();

                }
                catch (SharpDXException e)
                {
                    if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
                    {
                        throw e;
                    }
                }
            }

            // TODO: We should cleanp up all allocated COM objects here
        }
    }
}

我知道我可以将框架转换为 System.Drawing.Bitmap(就像在原始示例中一样),然后使用 System.Drawing.Graphics.DrawImage 将位图绘制到表单,但我想避免这种情况并绘制使用 SharpDX 的框架。 我也尝试过创建一个 SwapChain 并使用 Direct2D1.RenderTarget.DrawBitmap(类似于 MiniRect sample)绘制框架,但它失败并出现相同的异常。同样使用 Direct2D1.DeviceContext.DrawBitmap。 我读到 here 这个异常意味着我正在 混合来自不同资源域的资源 ,但我不明白它在我的代码中的确切含义。我没有使用 DirectX 的经验。

我再次尝试使用 SwapChain,而不是 WindowRenderTarget,这次我成功了。

  • 创建一个 SwapChainDescription 指定目标控件(例如窗体或面板)的宽度、高度和句柄。
  • 使用 dxgiFactoryd3dDeviceSwapChainDescription 创建一个新的 SwapChain
  • 获取 SwapChainback buffer
  • back buffer 中得到一个 DXGI Surface

(更多关于在 MiniRect sample 中创建 SwapChainback bufferDXGI Surface

  • 使用 d2dContextDXGI Surface 创建一个 Direct2D1.Bitmap1
  • 使用 d2dContextscreenResource 创建另一个 Direct2D1.Bitmap1(如上面的代码所示)。
  • 将第一个 Direct2D1.Bitmap1 设置为 d2dContext 的目标。
  • 使用d2dContext绘制第二个Direct2D1.Bitmap1
  • 呈现 SwapChain.
  • 完成。