截图并使用 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
指定目标控件(例如窗体或面板)的宽度、高度和句柄。
- 使用
dxgiFactory
、d3dDevice
和 SwapChainDescription
创建一个新的 SwapChain
。
- 获取
SwapChain
的 back buffer
。
- 从
back buffer
中得到一个 DXGI Surface
。
(更多关于在 MiniRect sample 中创建 SwapChain
、back buffer
和 DXGI Surface
)
- 使用
d2dContext
从 DXGI Surface
创建一个 Direct2D1.Bitmap1
。
- 使用
d2dContext
从 screenResource
创建另一个 Direct2D1.Bitmap1
(如上面的代码所示)。
- 将第一个
Direct2D1.Bitmap1
设置为 d2dContext
的目标。
- 使用
d2dContext
绘制第二个Direct2D1.Bitmap1
。
- 呈现
SwapChain
.
- 完成。
我正在尝试使用 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
指定目标控件(例如窗体或面板)的宽度、高度和句柄。 - 使用
dxgiFactory
、d3dDevice
和SwapChainDescription
创建一个新的SwapChain
。 - 获取
SwapChain
的back buffer
。 - 从
back buffer
中得到一个DXGI Surface
。
(更多关于在 MiniRect sample 中创建 SwapChain
、back buffer
和 DXGI Surface
)
- 使用
d2dContext
从DXGI Surface
创建一个Direct2D1.Bitmap1
。 - 使用
d2dContext
从screenResource
创建另一个Direct2D1.Bitmap1
(如上面的代码所示)。 - 将第一个
Direct2D1.Bitmap1
设置为d2dContext
的目标。 - 使用
d2dContext
绘制第二个Direct2D1.Bitmap1
。 - 呈现
SwapChain
. - 完成。