c# – 使用SharpDX和EasyHook捕获全屏DX11程序的截图

c# – 使用SharpDX和EasyHook捕获全屏DX11程序的截图,第1张

概述在任何人提到之前,我指向 this链接,以了解如何将backbuffer复制到位图. 现在的情况 >我被注入目标进程 >目标进程’FeatureLevel = Level_11_0 >目标SwapChain正在使用DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH标志. > SwapChain :: Present函数挂钩. >屏幕截图显示黑色和目标进程崩溃.没有截图过程 在任何人提到之前,我指向 this链接,以了解如何将backbuffer复制到位图.

现在的情况

>我被注入目标进程
>目标进程’FeatureLevel = Level_11_0
>目标SwapChain正在使用dxgi_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH标志.
> SwapChain :: Present函数挂钩.
>屏幕截图显示黑色和目标进程崩溃.没有截图过程运行正常.

期望的情况

使截图正确,让目标进程继续正常执行.

注意Hook类与链接中的相同.我只添加了一个UnmodifiableHook版本,它的名称说.我省略了所有不重要的位.

TestSwapChainHook.cs

using System;using System.Runtime.InteropServices;namespace Test{    public sealed class TestSwapChainHook : Idisposable    {        private enum IdxgiSwapChainVirtualtable        {            queryInterface = 0,AddRef = 1,Release = 2,SetPrivateData = 3,SetPrivateDataInterface = 4,GetPrivateData = 5,GetParent = 6,GetDevice = 7,Present = 8,GetBuffer = 9,SetFullscreenState = 10,GetFullscreenState = 11,GetDesc = 12,ResizeBuffers = 13,ResizeTarget = 14,GetContainingOutput = 15,GetFrameStatistics = 16,GetLastPresentCount = 17,}        public static Readonly int VIRTUAL_METHOD_COUNT_LEVEL_DEFAulT = 18;        private static IntPtr[] SWAP_CHAIN_VIRTUAL_table_ADDRESSES;        [UnmanagedFunctionPointer(CallingConvention.StdCall,CharSet = CharSet.Unicode,SetLastError = true)]        public delegate int dxgiSwapChainPresentDelegate(IntPtr thisPtr,uint syncInterval,SharpDX.dxgi.PresentFlags flags);        public delegate int dxgiSwapChainPresentHookDelegate(UnmodifiableHook<dxgiSwapChainPresentDelegate> hook,IntPtr thisPtr,SharpDX.dxgi.PresentFlags flags);        private dxgiSwapChainPresentHookDelegate _present;        private Hook<dxgiSwapChainPresentDelegate> presentHook;        static TestSwapChainHook()        {            SharpDX.dxgi.Rational rational = new SharpDX.dxgi.Rational(60,1);            SharpDX.dxgi.ModeDescription modeDescription = new SharpDX.dxgi.ModeDescription(100,100,rational,SharpDX.dxgi.Format.R8G8B8A8_Unorm);            SharpDX.dxgi.SampleDescription sampleDescription = new SharpDX.dxgi.SampleDescription(1,0);            using (SharpDX.@R_301_5087@.RenderForm renderForm = new SharpDX.@R_301_5087@.RenderForm())            {                SharpDX.dxgi.SwapChainDescription swapChainDescription = new SharpDX.dxgi.SwapChainDescription();                swapChainDescription.BufferCount = 1;                swapChainDescription.Flags = SharpDX.dxgi.SwapChainFlags.None;                swapChainDescription.IsWindowed = true;                swapChainDescription.ModeDescription = modeDescription;                swapChainDescription.OutputHandle = renderForm.Handle;                swapChainDescription.SampleDescription = sampleDescription;                swapChainDescription.SwapEffect = SharpDX.dxgi.SwapEffect.discard;                swapChainDescription.Usage = SharpDX.dxgi.Usage.rendertargetOutput;                SharpDX.Direct3D11.Device device = null;                SharpDX.dxgi.SwapChain swapChain = null;                SharpDX.Direct3D11.Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.HarDWare,SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport,swapChainDescription,out device,out swapChain);                try                {                    IntPtr swapChainVirtualtable = Marshal.ReadIntPtr(swapChain.NativePointer);                    SWAP_CHAIN_VIRTUAL_table_ADDRESSES = new IntPtr[VIRTUAL_METHOD_COUNT_LEVEL_DEFAulT];                    for (int x = 0; x < VIRTUAL_METHOD_COUNT_LEVEL_DEFAulT; x++)                    {                        SWAP_CHAIN_VIRTUAL_table_ADDRESSES[x] = Marshal.ReadIntPtr(swapChainVirtualtable,x * IntPtr.Size);                    }                    device.dispose();                    swapChain.dispose();                }                catch (Exception)                {                    if (device != null)                    {                        device.dispose();                    }                    if (swapChain != null)                    {                        swapChain.dispose();                    }                    throw;                }            }        }        public TestSwapChainHook()        {            this._present = null;            this.presentHook = new Hook<dxgiSwapChainPresentDelegate>(                        SWAP_CHAIN_VIRTUAL_table_ADDRESSES[(int)IdxgiSwapChainVirtualtable.Present],new dxgiSwapChainPresentDelegate(hookPresent),this);        }        public voID activate()        {            this.presentHook.activate();        }        public voID deactivate()        {            this.presentHook.deactivate();        }        private int hookPresent(IntPtr thisPtr,SharpDX.dxgi.PresentFlags flags)        {            lock (this.presentHook)            {                if (this._present == null)                {                    return this.presentHook.original(thisPtr,syncInterval,flags);                }                else                {                    return this._present(new UnmodifiableHook<dxgiSwapChainPresentDelegate>(this.presentHook),thisPtr,flags);                }            }        }        public dxgiSwapChainPresentHookDelegate present        {            get            {                lock (this.presentHook)                {                    return this._present;                }            }            set            {                lock (this.presentHook)                {                    this._present = value;                }            }        }    }}

使用代码

初始化

private TestSwapChain swapChainHook;private bool capture = false;private object captureLock = new object();this.swapChainHook = new TestSwapChainHook();this.swapChainHook.present = presentHook;this.swapChainHook.activate();

编辑

我用不同的方法来捕获this链接中描述的截图.不过我的屏幕截图如下:

现在,这似乎是我的转换设置或任何问题,但我无法找出我需要做什么来解决它.我知道我正在转换为位图的表面使用dxgi_FORMAT_R10G10B10A2_UnorM格式(32位,每种颜色10位,而我认为的是2).但是我不知道这在if循环中是如何工作的(跳过字节和东西).我只是简单的复制粘贴它.

新的钩子功能

private int presentHook(UnmodifiableHook<IdxgiSwapChainHook.dxgiSwapChainPresentDelegate> hook,SharpDX.dxgi.PresentFlags flags){    try    {        lock (this.captureLock)        {            if (this.capture)            {                SharpDX.dxgi.SwapChain swapChain = (SharpDX.dxgi.SwapChain)thisPtr;                using (SharpDX.Direct3D11.Texture2D backBuffer = swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))                {                    SharpDX.Direct3D11.Texture2DDescription texture2DDescription = backBuffer.Description;                    texture2DDescription.cpuAccessFlags = SharpDX.Direct3D11.cpuAccessFlags.Read;                    texture2DDescription.Usage = SharpDX.Direct3D11.ResourceUsage.Staging;                    texture2DDescription.OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None;                    texture2DDescription.BindFlags = SharpDX.Direct3D11.BindFlags.None;                    using (SharpDX.Direct3D11.Texture2D texture = new SharpDX.Direct3D11.Texture2D(backBuffer.Device,texture2DDescription))                    {                        //dxgi_FORMAT_R10G10B10A2_UnorM                        backBuffer.Device.ImmediateContext.copyResource(backBuffer,texture);                        using (SharpDX.dxgi.Surface surface = texture.queryInterface<SharpDX.dxgi.Surface>())                        {                            SharpDX.DataStream dataStream;                            SharpDX.DataRectangle map = surface.Map(SharpDX.dxgi.MapFlags.Read,out dataStream);                            try                            {                                byte[] pixelData = new byte[surface.Description.WIDth * surface.Description.Height * 4];                                int lines = (int)(dataStream.Length / map.Pitch);                                int dataCounter = 0;                                int actualWIDth = surface.Description.WIDth * 4;                                for (int y = 0; y < lines; y++)                                {                                    for (int x = 0; x < map.Pitch; x++)                                    {                                        if (x < actualWIDth)                                        {                                            pixelData[dataCounter++] = dataStream.Read<byte>();                                        }                                        else                                        {                                            dataStream.Read<byte>();                                        }                                    }                                }                                GCHandle handle = GCHandle.Alloc(pixelData,GCHandleType.Pinned);                                try                                {                                    using (Bitmap bitmap = new Bitmap(surface.Description.WIDth,surface.Description.Height,map.Pitch,PixelFormat.Format32bppArgb,handle.AddrOfPinnedobject()))                                    {                                        bitmap.Save(@"C:\Users\SOMEUSERname\Desktop\test.bmp");                                    }                                }                                finally                                {                                    if (handle.IsAllocated)                                    {                                        handle.Free();                                    }                                }                            }                            finally                            {                                surface.Unmap();                                dataStream.dispose();                            }                        }                    }                }                this.capture = false;            }        }    }    catch(Exception ex)    {        MessageBox.Show(ex.ToString());    }    return hook.original(thisPtr,flags);}

回答

结果dxgi_FORMAT_R10G10B10A2_UnorM格式是这种位格式:

A=AlphaB=blueG=greenR=redAABBBBBB BBBBGGGG GGGGGGRR RRRRRRRR

而格式32bppArgb是这样的字节顺序:

BGRA

所以最后的循环代码是:

while (pixelindex < pixelData.Length){    uint currentPixel = dataStream.Read<uint>();    uint r = (currentPixel & 0x3FF);    uint g = (currentPixel & 0xFFC00) >> 10;    uint b = (currentPixel & 0x3FF00000) >> 20;    uint a = (currentPixel & 0xC0000000) >> 30;    pixelData[pixelindex++] = (byte)(b >> 2);    pixelData[pixelindex++] = (byte)(g >> 2);    pixelData[pixelindex++] = (byte)(r >> 2);    pixelData[pixelindex++] = (byte)(a << 6);    while ((pixelindex % map.Pitch) >= actualWIDth)    {        dataStream.Read<byte>();        pixelindex++;    }}
解决方法 那个屏幕截图看起来像R10G10B10A2被塞进R8G8B8A8.我没有测试过你的代码,但我们应该有这个位布局
xxxxxxxx yyyyyyyy zzzzzzzz wwwwwwwwRRRRRRRR RRGGGGGG GGGGBBBB BBBBBBAA

您可以如下提取它们

byte x = data[ptr++];byte y = data[ptr++];byte z = data[ptr++];byte w = data[ptr++];int r = x << 2 | y >> 6;int g = (y & 0x3F) << 4 | z >> 4;int b = (z & 0xF) << 6 | w >> 2;int a = w & 0x3;

其中r,g,b现在有10位分辨率.如果要将其缩放到字节,可以使用(byte)(r>> 2)来实现.

更新

这将取代你的双for循环.我没有办法测试,所以我不想进一步推进,但我相信这个想法是正确的.最后一次检查应该跳过每行的填充字节.

while(dataCounter < pixelData.Length){    byte x = dataStream.Read<byte>();    byte y = dataStream.Read<byte>();    byte z = dataStream.Read<byte>();    byte w = dataStream.Read<byte>();    int r = x << 2 | y >> 6;    int g = (y & 0x3F) << 4 | z >> 4;    int b = (z & 0xF) << 6 | w >> 2;    int a = w & 0x3;    pixelData[dataCounter++] = (byte)(r >> 2);    pixelData[dataCounter++] = (byte)(g >> 2);    pixelData[dataCounter++] = (byte)(b >> 2);    pixelData[dataCounter++] = (byte)(a << 6);    while((dataCounter % map.Pitch) >= actualWIDth)    {        dataStream.Read<byte>();        dataCounter++;    }}
总结

以上是内存溢出为你收集整理的c# – 使用SharpDX和EasyHook捕获全屏DX11程序的截图全部内容,希望文章能够帮你解决c# – 使用SharpDX和EasyHook捕获全屏DX11程序的截图所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1260784.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存