Pulling Print Screen C#

There is a game called Tibia (in Window Mode same) that when trying to take a print screen the game ends up obfuscating the image leaving only the game screen all black independent if the print was originated from the PrintScreen key, or some function via C#code.

I have tried in some ways to capture the image of the game but without success.

I don't know exactly what they do that they can obfuscate from print only the game window in issue.

I tried two ways:

Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);

The second:

        public static Bitmap GetDesktopImage()
        {
            WIN32_API.SIZE size;

            IntPtr hDC = WIN32_API.GetDC(WIN32_API.GetDesktopWindow());
            IntPtr hMemDC = WIN32_API.CreateCompatibleDC(hDC);

            size.cx = WIN32_API.GetSystemMetrics(WIN32_API.SM_CXSCREEN);
            size.cy = WIN32_API.GetSystemMetrics(WIN32_API.SM_CYSCREEN);

            m_HBitmap = WIN32_API.CreateCompatibleBitmap(hDC, size.cx, size.cy);

            if (m_HBitmap != IntPtr.Zero)
            {
                IntPtr hOld = (IntPtr)WIN32_API.SelectObject(hMemDC, m_HBitmap);
                WIN32_API.BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, WIN32_API.SRCCOPY);
                WIN32_API.SelectObject(hMemDC, hOld);
                WIN32_API.DeleteDC(hMemDC);
                WIN32_API.ReleaseDC(WIN32_API.GetDesktopWindow(), hDC);
                return System.Drawing.Image.FromHbitmap(m_HBitmap);
            }
            return null;
        }

public class WIN32_API
    {
        public struct SIZE
        {
            public int cx;
            public int cy;
        }
        public const int SRCCOPY = 13369376;
        public const int SM_CXSCREEN = 0;
        public const int SM_CYSCREEN = 1;

        [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
        public static extern IntPtr DeleteDC(IntPtr hDc);

        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        public static extern IntPtr DeleteObject(IntPtr hDc);

        [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
        public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);

        [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

        [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll", EntryPoint = "SelectObject")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);

        [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
        public static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll", EntryPoint = "GetDC")]
        public static extern IntPtr GetDC(IntPtr ptr);

        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        public static extern int GetSystemMetrics(int abc);

        [DllImport("user32.dll", EntryPoint = "GetWindowDC")]
        public static extern IntPtr GetWindowDC(Int32 ptr);

        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
    }

Is there any other way to take print and avoid this obfuscation?

Author: CypherPotato, 2019-12-31

1 answers

You will not succeed without understanding how DirectX works before.

Games have a rendering environment isolated from the operating system, and in their code is using the Windows API to capture the image Stream on the screen to a dedicated buffer. This will even work on games that run in window (or full window games with no borders), since they are still being rendered in the operating system environment and not another.

When a Direct3D application enters in full screen mode (full full screen and not borderless window), it gains full access to the graphics hardware, thus creating a rendering environment totally isolated from the operating system. In this case, you will not be able to access the screen image [image buffer] without going through the video card before.

Nvidia hardware has ShadowPlay. AMD's have Relive. Windows 10 itself has the Xbox DVR. They all capture the image in isolation. The Xbox One does different why it gets the image after being displayed on the screen. The others capture before the image is displayed, after the processing of the video card.

Third-party recorders typically inject a library that ensures the buffer is copied to another destination, and can then capture the screen. They are not caught by anti-cheating systems because they are often known and the protectors add exceptions to them.

In your case, you should use a third-party API to capture image dedicated to game. If you want to venture out and write your own, remember that you must be safe to go through the game's anti-chat system.

Update

I also saw that the Tibia has an anti-screenshot mechanism , in which it blocks screenshots regardless of whether it is in full screen or in window. But here found a way to circumvent this problem by calling the Tibia window, sending the screenshot command and getting the image of the screenshot.

[DllImport("user32.dll")]
extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);

void KeyDown(IntPtr hwnd, int vk_key)
{
    PostMessage(hwnd, WM_KEYDOWN, vk_key, (int)((MapVirtualKey((uint)vk_key, 0)) * 0x10000 + 1));
}

...

KeyDown(hWnd, 0x50);

After that, the screenshot will be available at ...\AppData\Local\Tibia\packages\Tibia\screenshots. You can use I / O to get the file.

Note that you must associate the key P for screenshots in the game.

 5
Author: CypherPotato, 2020-01-03 19:26:41