Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to capture window contents of a Windows Store App in C#

I have a bit of code to capture windows desktop app contents and save to a Bitmap object in .NET. It uses User32.dll and Gdi32.dll (BitBlt) and works just fine. However, the code produces all-black bitmaps when I give the code a handle to a window that holds a Windows Store app. I'm not sure if this is a security feature or what. I cannot use the ScreenCapture api as the contents of the window, after being resized, are almost always taller/larger than the screen. Has anyone had any luck capturing window contents even when they're larger than the screen, for a Windows Store app?

EDIT: Just as a note I am trying to capture a different program's window, not my own program. My program can be assumed to be a Windows Console application in .NET 4.6.1 / C#

Also, I know that this must be possible somehow in Windows APIs, because the Aero Peek feature, where if you hover over the taskbar on the running program's icon shows the full height of the window, including offscreen components. (see tall window on right, set to 6000px much higher than my display)

see tall window on right, set to 6000px much higher than my display

like image 263
Richthofen Avatar asked Jun 20 '16 21:06

Richthofen


2 Answers

As of Windows 8.1, you can use Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap to render elements to a bitmap. There are a couple of caveats to this:

  1. You can capture elements that are offscreen, as long as they are in the XAML visual tree and have Visibility set to Visible and not Collapsed.
  2. Some elements, like video, won't be captured.

See the API for more details:

https://msdn.microsoft.com/library/windows/apps/xaml/windows.ui.xaml.media.imaging.rendertargetbitmap.aspx

like image 200
Daniel A. Thompson Avatar answered Nov 01 '22 08:11

Daniel A. Thompson


This might do the trick. Basically get the window handle to the app, call the native functions on it to figure out the app window position, provide those do the graphics class and copy from the screen.

class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);

    [DllImport("user32.dll")]
    public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);

    public struct Rect
    {
        public int Left { get; set; }
        public int Top { get; set; }
        public int Right { get; set; }
        public int Bottom { get; set; }
    }


    static void Main(string[] args)
    {
        /// Give this your app's process name.
        Process[] processes = Process.GetProcessesByName("yourapp");
        Process lol = processes[0];
        IntPtr ptr = lol.MainWindowHandle;
        Rect AppRect = new Rect();
        GetWindowRect(ptr, ref AppRect);
        Rectangle rect = new Rectangle(AppRect.Left, AppRect.Top, (AppRect.Right - AppRect.Left), (AppRect.Bottom - AppRect.Top));
        Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
        Graphics g = Graphics.FromImage(bmp);
        g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);

        // make sure temp directory is there or it will throw.
        bmp.Save(@"c:\temp\test.jpg", ImageFormat.Jpeg);
    }
}
like image 32
Mavi Domates Avatar answered Nov 01 '22 07:11

Mavi Domates