2016-06-29 5 views
10

Zanim ktokolwiek o tym wspomnia, odwołałem się do linku this, aby dowiedzieć się, w jaki sposób skopiować kopię zapasową do bitmapy.Zrzut ekranu z pełnoekranowym programem DX11 za pomocą SharpDX i EasyHook

Obecna sytuacja

  • ja wstrzykuje do procesu docelowego
  • procesu docelowego FeatureLevel = Level_11_0
  • docelowa SwapChain jest wykonany z flagą DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH.
  • SwapChain :: Obecna funkcja jest zawieszona.
  • Zrzut ekranu okazuje się czarny, a proces docelowy ulega awarii. bez obsługi zrzutu działa dobrze.

Pożądany sytuacja

Zrób zrzut ekranu prawidłowo i niech proces docelowy kontynuować normalnej realizacji.

Kod

klasa UWAGA Hak jest taka sama jak w linku. Dodałem tylko wersję UnmodifiableHook, która spełnia swoją nazwę. Pominąłem wszystkie nieważne bity.

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, uint syncInterval, 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.Windows.RenderForm renderForm = new SharpDX.Windows.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, uint syncInterval, 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, syncInterval, flags); 
       } 
      } 
     } 

     public DXGISwapChainPresentHookDelegate present 
     { 
      get 
      { 
       lock (this.presentHook) 
       { 
        return this._present; 
       } 
      } 
      set 
      { 
       lock (this.presentHook) 
       { 
        this._present = value; 
       } 
      } 
     } 
    } 
} 

pomocą kodu

inicjalizacji

private TestSwapChain swapChainHook; 
private bool capture = false; 
private object captureLock = new object(); 

this.swapChainHook = new TestSwapChainHook(); 
this.swapChainHook.present = presentHook; 
this.swapChainHook.activate(); 

EDIT

Użyłem innej metody do przechwycenia zrzutu opisanego w łączu this. Jednak mój screenshot okazuje się tak:

Rainbow image

Teraz to wydaje się być problem z moich ustawień konwersji lub cokolwiek, ale jestem w stanie dowiedzieć się, co dokładnie trzeba zrobić, aby to naprawić. Wiem, że powierzchnia, którą konwertuję do bitmapy, używa formatu DXGI_FORMAT_R10G10B10A2_UNORM (32-bitowe, 10 bitów na kolor i 2 na alfa, jak sądzę?). Ale nie jestem pewien, jak to działa w pętlach for (pomijając bajty i inne). Po prostu skopiowałem go.

nowa funkcja hak

private int presentHook(UnmodifiableHook<IDXGISwapChainHook.DXGISwapChainPresentDelegate> hook, IntPtr thisPtr, uint syncInterval, 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, syncInterval, flags); 
} 

Odpowiedź

Okazuje się, że format DXGI_FORMAT_R10G10B10A2_UNORM jest w tym formacie bitowe:

A=alpha 
B=blue 
G=green 
R=red 

AABBBBBB BBBBGGGG GGGGGGRR RRRRRRRR 

I Format32bppArgb jest w tej kolejności bajtów:

BGRA 

Więc ostateczny kod pętla będzie:

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++; 
    } 
} 

Odpowiedz

3

Ten zrzut ekranu nie wygląda R10G10B10A2 jest uzyskiwanie nadziewany na R8G8B8A8. Nie testowałem swój kod, ale powinniśmy mieć ten układ bitowego

xxxxxxxx yyyyyyyy zzzzzzzz wwwwwwww 
RRRRRRRR RRGGGGGG GGGGBBBB BBBBBBAA 

i można wyodrębnić je następująco

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; 

gdzie r, g, b mają teraz rozdzielczość 10 bitów. Jeśli chcesz przeskalować je do bajtów, możesz to zrobić za pomocą (byte) (r >> 2).

Aktualizacja

byłoby to zastąpienie Dwuosobowy do pętli. Nie mam możliwości przetestowania tego, więc nie chcę tego popychać dalej, ale uważam, że pomysł jest poprawny. Ostatnia kontrola powinna pomijać bajty dopełnienia w każdym wierszu.

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++; 
    } 

} 
+0

wow, dzięki, ktoś może mi pomóc. więc w jaki sposób mógłbym to przejrzeć? tylko pętla for gdzie x Neijwiert

+0

Nie do końca, nasze x są różne. Pętliłbyś się nad ptr: 'for (int ptr = 0; ptr

+0

Niestety nie mogę dzisiaj przetestować. czy mógłbyś zaadaptować mój kod, aby upewnić się, że mam rację? – Neijwiert

Powiązane problemy