Advertisement

Capturing a screenshot from window handle

Started by October 25, 2024 04:32 PM
0 comments, last by sp00kie 3 months, 1 week ago

Hello, Im having a bit of trouble trying to get a screenshot script to work. I am not a gamedev but i thought this is a kind of game related thing. Maybe someone here can help?

Right now i am taking a hwnd, creating swap chain and device then trying to capture the back buffer to get the pixels. Im using SharpDX. Alot of this code is stuff i have found online and tweaked. I think it works right now ( im not getting any invalid arg errors ), but all the output is black.

heres the code, I dont use c# much so idk how clean it will be 😬

using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using Device = SharpDX.Direct3D11.Device;

internal class DXCaptureScreenCap
{
    private Device device;
    private SwapChain swapChain;
    public void InitializeDeviceAndSwapChain(IntPtr windowHandle)
    {
        SwapChainDescription swapChainDescription = new SwapChainDescription()
        {
            BufferCount = 2,
            ModeDescription = new ModeDescription(500, 500, new Rational(60, 1), Format.B8G8R8A8_UNorm),
            IsWindowed = true,
            OutputHandle = windowHandle,
            SampleDescription = new SampleDescription(1, 0),
            SwapEffect = SwapEffect.Discard,
            Usage = Usage.RenderTargetOutput
        };

        Device.CreateWithSwapChain(
            SharpDX.Direct3D.DriverType.Hardware,
            DeviceCreationFlags.BgraSupport,
            swapChainDescription,
            out device,
            out swapChain
        );
    }

    public void CaptureBackBuffer()
    {
        if (swapChain == null || device == null)
        {
            throw new Exception("Swapchain or device is null");
        }

        // Obtain the back buffer texture from the swap chain
        using (var texture = Texture2D.FromSwapChain<Texture2D>(swapChain, 0))
        {
            Console.WriteLine(texture);

            Texture2DDescription description = texture.Description;

            Texture2DDescription texture2DDesc = new Texture2DDescription
            {
                Width = description.Width,
                Height = description.Height,
                MipLevels = 1,
                ArraySize = 1,
                Format = description.Format,
                Usage = ResourceUsage.Staging,
                BindFlags = BindFlags.None,
                CpuAccessFlags = CpuAccessFlags.Read,
                OptionFlags = ResourceOptionFlags.None,
                SampleDescription = new SampleDescription(1, 0) // Needs to be 1,0 or we will get a invalid arg error.
            };

            using (var stagingTexture = new Texture2D(device, texture2DDesc))
            {
                var stagingDescription = stagingTexture.Description;
                Console.WriteLine($"Staging: Width={stagingDescription.Width}, Height={stagingDescription.Height}, Format={stagingDescription.Format}");

                device.ImmediateContext.CopyResource(texture, stagingTexture);

                var dataBox = device.ImmediateContext.MapSubresource(stagingTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
                byte[] pixelData = new byte[stagingDescription.Width * stagingDescription.Height * 4];

                for (int y = 0; y < stagingDescription.Height; y++)
                {
                    IntPtr srcPtr = dataBox.DataPointer + (y * dataBox.RowPitch);
                    Marshal.Copy(srcPtr, pixelData, y * stagingDescription.Width * 4, stagingDescription.Width * 4);
                }


                device.ImmediateContext.UnmapSubresource(stagingTexture, 0);

                // Check for non black pixels
                bool hasColorData = false;

                for (int i = 0; i < pixelData.Length; i += 4)
                {
                    byte blue = pixelData[i];     
                    byte green = pixelData[i + 1]; 
                    byte red = pixelData[i + 2]; 

                    
                    if (red != 0 || green != 0 || blue != 0)
                    {
                        hasColorData = true;
                        break; // Exit after finding non black pixel
                    }
                }

                if(!hasColorData)
                {
                    Console.WriteLine("The data is all black");
                }
            }
        }
    }
}

I am currently calling it from main like :

        DXCaptureScreenCap screenCap = new DXCaptureScreenCap();
        screenCap.InitializeDeviceAndSwapChain(memman.ProcessWindowHandle);
        
        Console.WriteLine("Press Enter to capture the back buffer...");
        Console.ReadLine(); // Wait for user input
        
        try
        {
            screenCap.CaptureBackBuffer();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error capturing back buffer: {ex.Message}");
        }  
        
        Console.WriteLine("Screenshot captured and resources cleaned up.");

im currently getting this output:

window handle : 1443286
Press Enter to capture the back buffer...

SharpDX.Direct3D11.Texture2D
Staging: Width=500, Height=500, Format=B8G8R8A8_UNorm
The data is all black
Screenshot captured and resources cleaned up.

and ofc a completely black image. :(

Any help is appreciated.

This topic is closed to new replies.

Advertisement