0

Purpose: Mirror a window of an external process by capturing images from this window and presenting the images in a picturebox using a timer.

Problem: When my application is in focus, everything happens fine! But if I switch the focus to the other window (not from my application), the picturebox stops refreshing the images. When I focus on my application again, everything returns to normal.

Limitation: I need the picturebox to continue updating even though my application is in the background.

Timer to capture images and show in picturebox:

private void timer_picBOX_refresh_Tick(object sender, EventArgs e)
    {
        pictureBox1.BackgroundImage = PrintScreen.CaptureWindow(GAME_MainHandle);            
        pictureBox1.Refresh();            
    }

Class to capture specific window:

public class class_ScreenCapture
{
    public Image CaptureScreen()
    {
        return CaptureWindow(User32.GetDesktopWindow());
    }

    /// <summary>
    /// Creates an Image object containing a screen shot of a specific window
    /// </summary>
    public Image CaptureWindow(IntPtr handle, int imgX = 0, int imgY = 0, int largura = 0, int altura = 0)
    {
        // get te hDC of the target window
        IntPtr hdcSrc = User32.GetWindowDC(handle);
        // get the size
        User32.RECT windowRect = new User32.RECT();
        User32.GetWindowRect(handle, ref windowRect);

        if (largura == 0 || altura == 0)
        {
            largura = windowRect.right - windowRect.left;
            altura = windowRect.bottom - windowRect.top;
        }

        // create a device context we can copy to
        IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
        // create a bitmap we can copy it to,
        // using GetDeviceCaps to get the width/height
        IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, largura, altura);
        // select the bitmap object
        IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
        // bitblt over
        GDI32.BitBlt(hdcDest, 0, 0, largura, altura, hdcSrc, imgX, imgY, GDI32.SRCCOPY);
        // restore selection
        GDI32.SelectObject(hdcDest, hOld);
        // clean up 
        GDI32.DeleteDC(hdcDest);
        User32.ReleaseDC(handle, hdcSrc);

        // get a .NET image object for it
        Image img = Image.FromHbitmap(hBitmap);
        // free up the Bitmap object
        GDI32.DeleteObject(hBitmap);

        return img;
    }        

    /// <summary>
    /// Helper class containing Gdi32 API functions
    /// </summary>
    private class GDI32
    {

        public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter

        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
            int nWidth, int nHeight, IntPtr hObjectSource,
            int nXSrc, int nYSrc, int dwRop);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
            int nHeight);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteDC(IntPtr hDC);
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
    }

    /// <summary>
    /// Helper class containing User32 API functions
    /// </summary>
    private class User32
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);

    }
}
Shafick Cruz
  • 61
  • 1
  • 3
  • I think your Problem is from here `pictureBox1.BackgroundImage` change to `pictureBox1.Image` – MrUnknow May 30 '18 at 08:19
  • 1
    Could you please tell some more about the interaction on your desktop. Do you have 1 screen? and if so if you "lose focus" does this mean the application is not visible any more? Because you are capturing your desktop with GetDesktopWindow() so if the window you want to capture is not visible you cannot capture it and show it in your picturebox. – Niels de Schrijver May 30 '18 at 08:25
  • .background or .image no difference in result // There is no compilation error. Debug run fine. // The game window I'm capturing is in "windowed" mode // My application is only partially covered by the game window // I'm not capturing the "screen" but a specific window (by main handle and DC compatible). IMPORTANT: The problem does not occur with notepad, calc, webbrowser, etc. windows. Only occurs with the game window from which I am capturing the images (a directx game window) – Shafick Cruz May 30 '18 at 09:30
  • It's like if my application is visually frozen when focus is on the game window. – Shafick Cruz May 30 '18 at 09:49
  • I think the picturebox is actually updating but you dont see it because the images you are capturing from the game are always the same after the game got focus. I would try displaying random images in your picturebox to see if that is the case. Since DirectX renders directly to the screen it might not be possible to capture the game screen with the win api like that. Windows probably downloads the images from the graphics card when the game is not in focus to provide some kind of preview (e.g. in the taskbar) which is no longer needed when the game gets focus. – Mario Dekena May 30 '18 at 10:11
  • It sounds like to me that your trying to do a screen capture or recorder. I would make the Windows form transparent, and then add the control. That's how I made my desktop screen recorder for my coding in C# tutorials on YouTube. If you would like to see the code example I can show it in an answer. – Halonic May 30 '18 at 12:29
  • The picturebox image changes every 100ms by timer. I put it to save the image to file and then load with the picturebox and it is working properly. – Shafick Cruz May 30 '18 at 18:22
  • The problem is: the winform UI (visual) gets frozen when the focus is on the game window. The code stay working, but the UI get frozen. Only with this window type (directx game) – Shafick Cruz May 30 '18 at 18:25

1 Answers1

0

Test it.

private void timer_picBOX_refresh_Tick(object sender, EventArgs e)
{
    pictureBox1.Image = PrintScreen.CaptureWindow(GAME_MainHandle);            
    pictureBox1.Refresh();
    Refresh();
}