You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用WinRT截取非活跃窗口截图及解决CanvasBitmap创建异常

解决方案:Direct3D11CaptureFrame转可保存图像的异常修复与替代方案

异常原因分析

CanvasDevice.GetSharedDevice()抛出0x88990003异常,是因为该方法创建的设备与WinRT屏幕捕获框架使用的Direct3D设备不兼容,两者属于不同的设备上下文,无法直接共享资源。


方案一:兼容Win2D的设备创建方式

不要使用GetSharedDevice(),而是基于你已有的d3dDevice(SharpDX的Direct3D11.Device)创建与捕获帧池兼容的CanvasDevice:

修改OnFrameArrived方法中创建canvasDevice的代码:

private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
{
    var newSize = false;

    using (var frame = sender.TryGetNextFrame())
    {
        // 替换原有CanvasDevice.GetSharedDevice()的代码
        using var winrtD3dDevice = Direct3D11Helper.CreateDirect3DDevice(d3dDevice);
        var canvasDevice = CanvasDevice.CreateFromDirect3D11Device(winrtD3dDevice);

        // 现在可以正常转换表面
        _currentFrame = CanvasBitmap.CreateFromDirect3D11Surface(canvasDevice, frame.Surface);
        
        // 后续原有代码保持不变...
        if (frame.ContentSize.Width != lastSize.Width ||
            frame.ContentSize.Height != lastSize.Height)
        {
            newSize = true;
            lastSize = frame.ContentSize;
            swapChain.ResizeBuffers(
                2, 
                lastSize.Width, 
                lastSize.Height,
                Format.B8G8R8A8_UNorm, 
                SwapChainFlags.None);
        }

        using (var backBuffer = swapChain.GetBackBuffer<Texture2D>(0))
        using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
        {
            d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);
        }

    }

    swapChain.Present(0, PresentFlags.None);

    if (newSize)
    {
        framePool.Recreate(
            device,
            DirectXPixelFormat.B8G8R8A8UIntNormalized,
            2,
            lastSize);
    }
}

通过Direct3D11Helper.CreateDirect3DDevice将SharpDX设备转为WinRT兼容的IDirect3DDevice,再创建对应的CanvasDevice,就能与捕获的frame.Surface资源兼容,避免异常。


方案二:无需Win2D,直接用SharpDX读取像素保存图像

如果不需要Win2D的后续处理,可以直接读取捕获到的Texture2D像素数据,转换成标准图像格式保存:

在CopyResource之后添加以下代码:

using (var backBuffer = swapChain.GetBackBuffer<Texture2D>(0))
using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
{
    d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);

    // 读取纹理像素数据
    var subresource = d3dDevice.ImmediateContext.MapSubresource(bitmap, 0, MapMode.Read, MapFlags.None);
    try
    {
        // 创建GDI Bitmap(注意像素格式转换:D3D是B8G8R8A8,GDI是ARGB)
        var outputBitmap = new Bitmap(
            (int)lastSize.Width, 
            (int)lastSize.Height, 
            System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
        
        var bitmapData = outputBitmap.LockBits(
            new Rectangle(0, 0, outputBitmap.Width, outputBitmap.Height),
            ImageLockMode.WriteOnly,
            outputBitmap.PixelFormat);
        
        try
        {
            // 逐行复制并转换通道顺序
            for (int y = 0; y < outputBitmap.Height; y++)
            {
                IntPtr srcRow = subresource.DataPointer + y * subresource.RowPitch;
                IntPtr destRow = bitmapData.Scan0 + y * bitmapData.Stride;
                
                // 每行复制并转换BGR到RGB
                for (int x = 0; x < outputBitmap.Width; x++)
                {
                    byte b = Marshal.ReadByte(srcRow, x * 4);
                    byte g = Marshal.ReadByte(srcRow, x * 4 + 1);
                    byte r = Marshal.ReadByte(srcRow, x * 4 + 2);
                    byte a = Marshal.ReadByte(srcRow, x * 4 + 3);
                    
                    Marshal.WriteByte(destRow, x * 4, r);
                    Marshal.WriteByte(destRow, x * 4 + 1, g);
                    Marshal.WriteByte(destRow, x * 4 + 2, b);
                    Marshal.WriteByte(destRow, x * 4 + 3, a);
                }
            }
            
            // 保存图像
            outputBitmap.Save("window_capture.png", ImageFormat.Png);
        }
        finally
        {
            outputBitmap.UnlockBits(bitmapData);
        }
    }
    finally
    {
        d3dDevice.ImmediateContext.UnmapSubresource(bitmap, 0);
    }
}

注意事项

  • 需引用System.Drawing.Common包(.NET 6+需手动安装)
  • 像素格式转换不可省略,否则图像颜色会出现颠倒

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:07:08